Skip to main content


AutoInstall


O Ubuntu 20.04 introduziu um novo sistema de instalação automática. Antes disso, a Canonical utilizava o mesmo método adotado até hoje pelo Debian. Vale ressaltar que esse novo sistema foi desenvolvido com foco em ambientes de nuvem (Cloud), mas pode ser facilmente utilizado também em instalações convencionais, fora de ambientes de Cloud.


O AutoInstall do Ubuntu funciona por meio de uma configuração do Cloud-Init, cujo objetivo é responder automaticamente às perguntas feitas durante o processo de instalação, fazendo com que ele seja completamente automatizado.


Para que o AutoInstall funcione, é necessário fornecer dois arquivos chamados user-data e meta-data, normalmente disponibilizados via NoCloud.



Cloud-Init


O Cloud-Init é uma ferramenta criada pela Canonical que permite personalizar uma imagem de sistema operacional Linux durante o processo de inicialização. Com ele, é possível instalar pacotes, criar usuários, configurar rede, ajustar permissões e executar comandos automaticamente, tornando a configuração inicial totalmente automatizada.


Durante a primeira inicialização de um novo sistema, o cloud-init lê as configurações definidas em arquivos YAML e as aplica automaticamente. Esse processo ocorre apenas na configuração inicial do sistema e facilita a implantação de múltiplas instâncias em ambientes de nuvem ou em qualquer cenário que exija inicializações padronizadas.



NoCloud


O NoCloud é uma fonte de dados (datasource) que permite fornecer os arquivos user-data e meta-data sem depender de um serviço de rede ou automação externa. Essa abordagem é especialmente útil em instalações locais ou em ambientes sem acesso a infraestrutura de nuvem.


Existem algumas formas de disponibilizar esses arquivos:

  • ISO ou VFAT

    Os arquivos podem ser fornecidos por meio de um sistema de arquivos ISO ou vFAT durante o boot local. Em ambos os casos, os arquivos user-data e meta-data devem estar presentes dentro do sistema de arquivos, e no caso da ISO, o rótulo (label) deve ser cidata ou CIDATA.

  • Linha de comando (CLI)

    Também é possível fornecer esses arquivos via parâmetros de kernel durante a inicialização da instalação. Para isso, utilize as opções abaixo:

    ## Na linha do kernel:

    # Para arquivos locais:
    autoinstall ds=nocloud;KEY=VALUE

    # Para arquivos remotos:
    autoinstall ds=nocloud-net;KEY=VALUE

O ds=nocloud é usado para obter os arquivos localmente, via File System ISO ou VFAT. Já o ds=nocloud-net é usado para obter os arquivos usando algum tipo de repositório ou FTP.


No lugar de KEY vamos usar a letra s que significa o mesmo que seedfrom, além da letra s existem mais duas opções, veja aqui. Em ds=nocloud o valor da chave s deve começar com / ou file://, igual o exemplo abaixo:

# Exemplo com '/':
ds=nocloud;s=/cdrom/nocloud/

# Exemplo com 'file://':
ds=nocloud;s=file://cdrom/nocloud/

# Como nesse caso 'nocloud' é um diretório,
# ele deve ter uma barra no final!

Já com o uso de ds=nocloud-net o valor de s deve começar com http:// ou https://, veja abaixo um exemplo:

# Exemplo com 'http://':
ds=nocloud-net;s=http://my.tftp.example.com.br/nocloud/

# Exemplo com 'https://':
ds=nocloud-net;s=http://my.tftp.example.com.br/nocloud/

# Como nesse caso 'nocloud' é um diretório
# (apenas um exemplo nesse caso), ele deve ter uma barra no final!

Caso esteja usando o sistema no padrão UEFI, você deve escapar o ; (ponto e vírgula) usando " (aspas) ou ' (apóstrofo), veja um exemplo abaixo:

ds="nocloud;s=/cdrom/nocloud/"

ds="nocloud-net;s=http://my.tftp.example.com.br/nocloud/"

# ou, usando contra barra '\'

ds=nocloud\;s=/cdrom/nocloud/

ds=nocloud-net\;s=http://my.tftp.example.com.br/nocloud/

Caso isso não seja feito, irá dar erro no processo porque o UEFI não vai conseguir interpretar de maneira adequada.


Somente dois arquivos são necessários para o Auto Install acontecer, o user-data contém informações para a automação e o meta-data que pode e normalmente estará vazio, serve para identificar metadata de propriedades de um objeto. Caso esse arquivo (que estará vazio por nossa parte) não esteja presente irá dar erro no processo.

O user-data segue a estrutura do YAML.



Opções do User-Data


Eu sei que toda essa teoria é um pouco cansativa e chata, mas é importante entender algumas das opções utilizadas no user-data. Vou explicar as principais opções, essas que é certeza que você vai precisar usar.


Você pode ler a lista completa de parâmetros disponíveis para o AutoInstall na documentação oficial.



Storage


A opção Storage é usada para criar um layout de disco no sistema que será instalado. Em sistema com mais de um disco de armazenamento será usado o maior disco dentre todos do sistema com o padrão abaixo:


PartiçãoDescrição
Bios BootPartição de 1MB de espaço no início do disco (disco formatado com GPT)
/bootMais ou menos com 1.5GB
/Com o restante do disco (usando LVM)
ATENÇÃO

No final do tutorial existem alguns exemplos variados de layout de disco que podem ser usados, analise caso a caso e customize caso necessário.


A opção Storage trabalha com trêd métodos para configurar o disco (pelo menos até a criação desse conteúdo).



Layout


O campo layout dentro da seção storage serve para definir automaticamente o particionamento e a estrutura de armazenamento do sistema, sem precisar descrever cada partição manualmente.


Dentro da seção layout temos as três opções de uso, que vão ditar como o instalador vai configurar o particionamento e a estrutura de armazenamento do sistema.



LVM

Usar o layout com lvm formata o disco seguindo o layout abaixo:

PartiçãoDescrição
Bios BootPartição de 1 MB localizada no início do disco (utilizada em discos com tabela de partição GPT)
/bootAproximadamente 1,5 GB
/Todo o espaço restante do disco (usando LVM)

A configuração abaixo é do arquivo user-data:

storage:
layout:
name: lvm


DIRECT

Usar o layout com direct formata o disco criando apenas uma partição raiz (/) e uma pequena partição BIOS Boot (quando o disco usa GPT).

PartiçãoDescrição
Bios BootPartição de 1 MB localizada no início do disco (necessária em discos com tabela de partição GPT)
/Todo o espaço restante do disco (sem LVM)

A configuração abaixo é do arquivo user-data:

storage:
layout:
name: direct


ZFS

Formata o disco usando o ZFS (Zettabyte File System). Ele é um sistema de arquivos avançado que combina gerenciamento de volumes e sistema de arquivos em uma única camada. Ao escolher esse layout, o instalador cria automaticamente um pool ZFS e organiza os datasets principais do sistema dentro dele.


O layout padrão criado segue aproximadamente esta estrutura:

PartiçãoDescrição
Bios Boot / EFIPartição de 1 MB (GPT) ou partição EFI, conforme o modo de boot
rpoolPool principal do ZFS, contendo os datasets do sistema
rpool/ROOT/ubuntuDataset raiz (montado em /)
rpool/USERDATA/Área onde ficam os dados de usuários e configurações adicionais

A configuração abaixo é do arquivo user-data:

storage:
layout:
name: zfs

Esse layout é útil quando se deseja aproveitar recursos como snapshots, compressão, deduplicação, rollback de sistema e verificação de integridade nativa do ZFS. Em contrapartida, ele consome mais memória RAM e pode não ser ideal para ambientes com recursos limitados.



MATCH

O match é usado para selecionar qual disco será utilizado durante a instalação. Essa opção é especialmente útil em sistemas com múltiplos discos, permitindo que você escolha o destino com base em atributos específicos do dispositivo.


Exemplo por número de série:

storage:
layout:
name: lvm
match:
serial: CT*

Exemplo selecionando apenas discos SSD:

storage:
layout:
name: lvm
match:
ssd: yes

O match pode usar outros filtros, como size:, model:, vendor:, entre outros. Se nenhum filtro for definido, o instalador escolherá automaticamente o maior disco disponível.



Swap

A opção storage declara a configuração de swap. Essa opção define o tamanho do arquivo de swap que será criado no sistema.

storage:
swap:
size: 0

Para desativar o swap file, defina size: 0. Para criar um arquivo de swap, defina o valor desejado, por exemplo size: 5GB.


Se a opção swap não for declarada, o Curtin tentará determinar automaticamente um valor adequado com base na capacidade de armazenamento do sistema. Em um sistema de aproximadamente 25 GB, o tamanho gerado automaticamente costuma ficar em torno de 4,5 GB.


CURTIN

O Curtin (abreviação de "curt installer", é o motor de instalação de baixo nível usado pelo Subiquity e pelo sistema de AutoInstall do Ubuntu.


Ele é o componente responsável por realmente escrever o sistema no disco, ou seja, é o Curtin quem executa as ações que o cloud-init ou o autoinstall descrevem.


Basicamente o Curtin faz:

  • Cria e formata partições, volumes LVM e RAID.
  • Configura o sistema de arquivos e os pontos de montagem.
  • Instala o sistema base do Ubuntu.
  • Aplica configurações de rede, bootloader e swap.
  • Executa comandos personalizados (antes e depois da instalação, via hooks).


Config


Além do layout, existe o método chamado config, que oferece uma forma mais detalhada e flexível de configurar o disco. Esse método permite criar partições manualmente, definir métodos de formatação e aplicar configurações mais avançadas, como RAID, LVM, ZFS e até múltiplos discos.


Dentro da seção config, a configuração do disco é organizada em sessões identificadas por IDs, o que facilita a referência entre as etapas. Normalmente, o processo segue esta ordem:

  1. Especificar o disco que será utilizado;
  2. Definir as partições;
  3. Formatar as partições criadas;
  4. Montar as partições formatadas.


Especificar o disco que será utilizado

Vamos criar a primeira sessão dentro de config, onde declaramos qual disco será utilizado.

config:
- id: disk0
type: disk
ptable: msdos
name: main_disk
wipe: superblock
grub_device: true

Explicação dos parâmetros acima:

  • id: define um identificador único para esta sessão (usado para referenciar o disco em outros pontos da configuração).
  • type: especifica que esta sessão representa um dispositivo de disco.
  • ptable: define o tipo de tabela de partição, neste caso, msdos.
  • name: cria um link simbólico no udev com esse nome.
  • wipe: indica como o disco será limpo antes da configuração (superblock é a opção mais comum).
  • grub_device: informa que o GRUB será instalado neste disco.

Dentro dessa mesma sessão, também podemos usar a opção path: /dev/sda para especificar explicitamente qual disco físico será usado. Outra alternativa é o uso de serial: CT*, onde "CT" representa o início do número de série do disco, útil em ambientes com vários discos e nomes de dispositivos dinâmicos.


Também é possível selecionar discos via SCSI, mas essa opção é menos comum e não será detalhada aqui.



Definir as partições

Agora veremos um exemplo de como criar as partições. Os pontos (...) indicam a omissão da sessão anterior, onde o disco foi declarado.

config:
...
- id: disk0-root
type: partition
number: 1
size: -1
device: disk0

Explicação dos parâmetros:

  • id: define um identificador único para esta partição.
  • type: indica que esta sessão corresponde a uma partição.
  • number: especifica o número da partição (pode ser omitido, e o instalador atribuirá automaticamente).
  • size: define o tamanho da partição. O valor -1 indica que todo o espaço restante do disco será utilizado.
  • device: referencia o ID da sessão de disco criada anteriormente, informando que esta partição pertence àquele disco.

Além desses parâmetros básicos, podemos usar algumas opções adicionais importantes:

  • FLAG

    Define flags de partição, utilizadas para indicar funções ou características específicas. As opções disponíveis são: logical, extended, boot, bios_grub, swap, lvm, raid, home, prep.

  • BOOT

    Usada para adicionar a flag de boot a uma partição. Em discos com tabela de partição MSDOS, adiciona a flag de boot correspondente. Em discos com tabela GPT, adiciona a flag ESP (EFI System Partition).

    Deve ser usada apenas na partição de boot.

  • BIOS_GRUB

    Utilizada para criar a partição de 1 MB no início do disco, necessária quando se usa GPT em sistemas que ainda dão suporte ao modo BIOS/MBR.

    Caso o disco utilize GPT, o uso dessa flag é obrigatório.



Formatar as partições criadas

Agora vamos formatar a partição. Novamente, os pontos (...) indicam a omissão das sessões anteriores, onde o disco e a partição foram declarados.

config:
...
- id: disk0-root-format
type: format
fstype: ext4
volume: disk0-root

Explicação dos parâmetros:

  • id: define o identificador único dessa sessão.
  • type: indica que esta sessão está relacionada à formatação de partições.
  • fstype: especifica o sistema de arquivos que será utilizado (neste exemplo, ext4).
  • volume: faz referência ao ID da sessão de partição criada anteriormente, indicando qual partição será formatada.

O parâmetro volume é obrigatório, pois informa ao instalador qual partição está sendo formatada dentro do processo de configuração.



Montar as partições formatadas

Agora vamos montar as partições. Como de costume, os pontos (...) indicam a omissão das sessões anteriores, onde o disco, a partição e a formatação foram definidos.

config:
...
- id: disk0-root-mount
type: mount
path: /
device: disk0-root-format

Explicação dos parâmetros:

  • id: define o identificador único para esta sessão.
  • type: indica que esta sessão está relacionada à montagem de partições.
  • path: especifica o ponto de montagem da partição (neste exemplo, /).
  • device: faz referência ao ID da sessão de formatação, informando ao sistema qual partição deve ser montada.

Para partições do tipo swap, o parâmetro path deve ser definido como path: none.


Essas sessões são interdependentes, cada uma depende da anterior para que o processo de instalação funcione corretamente. Um erro em qualquer etapa pode fazer com que a instalação falhe completamente, já que o instalador segue essa sequência de forma rigorosa.



Locale


O Locale define as configurações regionais e de idioma do sistema. Ele controla aspectos como idioma padrão, formato de data e hora, formatação de números, moeda e ordenação de caracteres (collation).


Durante o processo de instalação, a opção locale também define a variável de ambiente $LANG, que será usada globalmente pelo sistema após o primeiro boot. Essa variável influencia como mensagens, menus, logs e programas exibem texto.


Exemplo de configuração:

locale: en_US.UTF-8

Neste exemplo, o en_US indica o idioma (inglês) e o país (Estados Unidos), já o UTF-8 define a codificação de caracteres usada pelo sistema (Unicode).


Abaixo deixo alguns exemplos de locale:

IdiomaLocaleDescrição
Português (Brasil)pt_BR.UTF-8Exibe o sistema em português, com formato de data e moeda brasileiros
Inglês (EUA)en_US.UTF-8Padrão em sistemas internacionais
Espanhol (Espanha)es_ES.UTF-8Sistema em espanhol com formatação europeia
Francês (França)fr_FR.UTF-8Sistema em francês, datas e moedas da França


Keyboard


Configura o layout do teclado. Exemplo:

Já tive problema do Gnome ignorar isso e o que foi definido aqui passou a funcionar apenas em modo texto (tive que alterar no próprio Gnome).

keyboard:
layout: br

## layout = Define a variável $XKBLAYOUT.
# O valor 'br' é o mesmo que ter um teclado 'abnt2'.


Processo para o Auto Install


Para que o processo de instalação automática do sistema seja mais rápido, é recomendado utilizar a imagem Daily Build do Ubuntu. Essas imagens contêm todos os patches e atualizações lançados até o momento em que foram geradas, reduzindo a necessidade de baixar correções durante a instalação.


Caso uma imagem antiga seja usada, o instalador precisará baixar e aplicar as atualizações (principalmente de segurança) durante o processo, o que torna a instalação mais demorada.



Processo para Criação de uma ISO (sem uso de PXE)


O processo abaixo mostra como criar uma imagem ISO personalizada com suporte ao Auto Install. Essa ISO será usada para realizar instalações automáticas do Ubuntu em ambientes locais ou virtuais.


  1. Descompacte a ISO original do Ubuntu;
  2. Crie uma pasta chamada nocloud dentro da ISO;
  3. Conceda permissão de escrita à pasta nocloud e, em seguida, crie ou copie os arquivos user-data e meta-data dentro dela;
  4. Dê permissão de escrita ao arquivo txt.cfg (responsável pelo menu de boot);
  5. Adicione um novo label dentro de txt.cfg, contendo as opções para o Auto Install;
  6. Remova as permissões de escrita da pasta nocloud (e dos arquivos dentro dela) e também de txt.cfg;
  7. Conceda permissão de escrita ao arquivo md5sum.txt;
  8. Atualize os hashes MD5 dos arquivos da ISO;
  9. Remova novamente a permissão de escrita de md5sum.txt;
  10. Gere a nova imagem ISO com as modificações aplicadas.

Agora que o processo foi descrito, vamos criar uma imagem customizada utilizando esses passos. Essa imagem servirá como base para instalações automatizadas do Ubuntu, sem necessidade de interação durante a instalação.


Essa ISO também pode ser usada em conjunto com o PXE, embora esse método exija um pouco mais de configuração. Mais adiante, veremos uma forma mais simples e recomendada para uso com PXE. O passo a passo abaixo é especialmente indicado para ambientes de virtualização local.

ATENÇÃO

Essa imagem personalizada não deve ser gravada em mídias físicas, como CD, DVD ou pendrive (Flash Drive). Ela é destinada exclusivamente a ambientes virtuais ou instalações via PXE.


# Baixa a imagem mais recente do ubuntu:
$› wget https://cdimage.ubuntu.com/ubuntu-server/focal/daily-live/current/focal-live-server-amd64.iso

# Crie uma pasta onde vamos colocar o conteúdo da ISO:
$› mkdir isofiles

# Extraia a ISO para a pasta que acabamos de criar:
$› bsdtar -C isofiles -xf focal-live-server-amd64.iso

# Crie uma pasta onde vamos deixar os arquivos necessários para a instalação automática:
$› mkdir isofiles/nocloud

# Dê permissão de escrita para a pasta 'nocloud', onde vamos colocar os arquivos
# necessários para a instalação e de permissão também para o arquivo 'txt.cfg' onde
# vamos criar um novo Label para iniciar o sistema informando parâmetros adicionais:
$› chmod +w -R isofiles/isolinux/txt.cfg
$› chmod +w -R isofiles/nocloud/

# Crie os arquivos 'meta-data' e 'user-data' dentro de 'nocloud',
# Para o 'user-data', use o exemplo na sessão 'USER-DATA':
$› vim isofiles/nocloud/user-data
$› touch isofiles/nocloud/meta-data

# Edite o arquivo 'txt.cfg' (Para padrão MBR):
$› vim isofiles/isolinux/txt.cfg
### Adicione a saída abaixo dentro desse arquivo ###
default autoinstall
label autoinstall
menu label ^Auto Install Ubuntu Server
kernel /casper/vmlinuz
append initrd=/casper/initrd autoinstall ds=nocloud;s=/cdrom/nocloud/ quiet ---
####################################################


# Edite o arquivo 'txt.cfg' (Para padrão UEFI):
$› vim isofiles/isolinux/txt.cfg
### Adicione a saída abaixo dentro desse arquivo ###
default autoinstall
label autoinstall
menu label ^Auto Install Ubuntu Server
kernel /casper/vmlinuz
append initrd=/casper/initrd autoinstall ds="nocloud;s=/cdrom/nocloud/" quiet ---
####################################################


# Remova as permissões de escrita que demos:
$› chmod -w -R isofiles/isolinux/txt.cfg
$› chmod -w -R isofiles/nocloud/

# Entre na pasta isofiles:
$› cd isofiles/

# Dê permissão de escrita no arquivo 'md5sum.txt':
$› chmod +w md5sum.txt

# Agora adicione nesse arquivo o novo hash dos arquivos que editamos, o comando abaixo simplifica tudo:
$› find -follow -type f ! -name md5sum.txt -print0 | xargs -0 md5sum > md5sum.txt

# Remova a permissão de escrita do arquivo acima:
$› chmod -w md5sum.txt

# Volte para a pasta anterior:
$› cd -

# Recrie uma imagem ISO com o conteúdo customizado:
$› sudo genisoimage -r -J -b isolinux/isolinux.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-o ubuntu-server-custom_autoinstall.iso isofiles

Ao iniciar o sistema via ISO, pressione ESC e escolha o menu que foi criado (vai ver o Label que foi adicionado).

ATENÇÃO

Lembrando que essa ISO não serve para ser "queimada" numa mídia física.



Configurando AutoInstall com PXE


Vamos fazer todo o procedimento para fazer o Auto Install usando o PXE (usando o Ubuntu 20.04). O PXELINUX é derivado do SysLinux, usado para inicializar o Linux através de um servidor de rede. O arquivo do pxelinux em sí é um bootloader para Linux que usa o protocolo de inicialização de rede PXE.


ATENÇÃO

O processo de criação de um PXE para UEFI é totalmente diferente do mostrado nesse tutorial.
Você pode conferir como subir um PXE para sistemas UEFI aqui

Vamos começar instalando os pacotes necessários para o funcionamento do pxelinux:

# Instalar as aplicações para o pxe server:
$› sudo apt install isc-dhcp-server nfs-kernel-server tftpd-hpa pxelinux -y

# isc-dhcp-server = Servidor DHCP;
# nfs-kernel-server = Servidor NFS;
# tftpd-hpa = Servidor TFTP-HPA;
# pxelinux = Pacote que contém os arquivos do PXE.

# Apenas para testar o TFTP, podemos instalar o pacote tftp-hpa.

Agora vamos configurar o TFTPD:

# Edite o arquivo de configuração do tftpd:
$› sudo vim /etc/default/tftpd-hpa

###### Deixe como abaixo, mudando o necessário ######
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS="192.168.100.2:69"
TFTP_OPTIONS="--secure"

# Você logo pode ver a diferença entre o meu arquivo e o seu,
# no meu foi modificado a pasta raiz do tftp e ao invés de deixar ele
# escutando em todas as interfaces, eu pedi para que ele escute apenas
# na interface 'enp0s8' que tem o IP '192.168.100.2'.

# Veja abaixo o ip na interface:
$› sudo ip addr show enp0s8
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:54:a2:67 brd ff:ff:ff:ff:ff:ff
inet 192.168.100.2/24 brd 192.168.100.255 scope global enp0s8
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fe54:a267/64 scope link
valid_lft forever preferred_lft forever

# Agora vamos criar a pasta do tftp:
$› sudo mkdir -p /srv/tftp

# Agora reinicie o serviço do tftpd e verifique o status:
$› sudo systemctl restart tftpd-hpa.service
$› sudo systemctl status tftpd-hpa.service

Agora vamos configurar o servidor DHCP, para uma configuração básica do dhcp, podemos usar o exemplo que está no arquivo /usr/share/doc/pxelinux/README.txt.gz caso você tenha o pacote pxelinux instalado, nosso exemplo foi baseado nesse arquivo, mudando apenas algumas coisas.

default-lease-time 600;
max-lease-time 7200;

allow booting;
allow bootp;

authoritative;
ignore client-updates;

log-facility local7;

subnet 192.168.100.0 netmask 255.255.255.0 {
range dynamic-bootp 192.168.100.20 192.168.100.253;
option broadcast-address 192.168.100.255;
option routers 192.168.100.2;
option domain-name-servers 8.8.8.8;
option subnet-mask 255.255.255.0;
default-lease-time 21600;
max-lease-time 43200;
}


group {
next-server 192.168.100.2;

host teste {
# Coloque o MAC da máquina cliente aqui em baixo!
hardware ethernet 52:54:00:20:35:ac;
fixed-address 192.168.100.177;
# Graças ao CHROOT usado pelo tftp-hpa, não precisamos colocar o caminho
# completo para o arquivo PXELINUX, pois o mesmo está no diretório raiz
# do tftp, caso contrário precisa colocar.
filename "pxe5254";

}
}

## Agora vamos configurar a interface do dhcp que ele vai ficar escutando
# Edite o arquivo abaixo:
$› sudo vim /etc/default/isc-dhcp-server

# Em INTERFACES, coloque o nome da interface que o daemon vai escutar, no meu caso
# a interface é 'enp0s8', segue meu exemplo:
INTERFACES="enp0s8"


# Agora reinicie o DHCP e veja o status do serviço:
$› sudo systemctl restart isc-dhcp-server
$› sudo systemctl status isc-dhcp-server

Agora vamos baixar a imagem do sistema que será instalada pela rede e vamos criar uma pasta onde vamos deixar os arquivos da ISO para que possam ser disponibilizados via NFS:

# Crie a pasta onde vamos colocar a ISO (descompactada):
$› sudo mkdir -p /distros/ubuntu2004

# Baixe a ISO, no nosso caso será uma uma imagem do Ubuntu 20.04
$› wget https://cdimage.ubuntu.com/ubuntu-server/focal/daily-live/current/focal-live-server-amd64.iso

# Agora vamos montar a imagem em /mnt:
$› sudo mount focal-live-server-amd64.iso /mnt/

# Agora copie tudo da pasta /mnt para a pasta /distros/ubuntu2004
$› sudo find /mnt -maxdepth 1 -not -wholename "/mnt" -exec cp -Rv {} /distros/ubuntu2004/ \;

Agora vamos compartilhar a pasta onde está nossa imagem do Ubuntu para que ela seja disponibilizada durante o boot:

# Edite o arquivo:
$› sudo vim /etc/exports

# Deixe como abaixo:
/distros/ubuntu2004 *(rw,sync,no_subtree_check)


# Exporte a configuração do NFS
$› sudo exportfs -a

Toda a configuração do pxe (arquivos e binários) é colocada dentro do tftp, para que assim o tftp possa fornecer os arquivos como pxelinux.0, initrd, vmlinuz entre outros.

## Crie a pasta para colocarmos o pxelinux.0:
$› sudo mkdir /srv/tftp/pxe/

## Vamos pegar o pxelinux.0:
$› sudo cp -v /usr/lib/PXELINUX/pxelinux.0 /srv/tftp/pxe/

## Crie a pasta onde colocaremos o arquivo de configuração padrão:
$› sudo mkdir /srv/tftp/pxelinux.cfg

## Crie o arquivo de configuração padrão:
$› sudo touch /srv/tftp/pxelinux.cfg/default

## Edite o arquivo:
$› sudo vim /srv/tftp/pxelinux.cfg/default

#### Coloque a informação abaixo: ####
default install
prompt 0
timeout 1
label install
menu label Install
kernel vmlinuz
append initrd=initrd ip=dhcp nfsroot=192.168.100.2:/distros/ubuntu2004/ netboot=nfs ro autoinstall ds=nocloud;s=/cdrom/nocloud/ ---


## Vamos colar a imagem do kernel e o initrd:
$› sudo cp /mnt/casper/{vmlinuz,initrd} /srv/tftp/

# Agora vamos copiar para o tftp o arquivo de bios simples:
$› sudo cp -v /usr/lib/syslinux/modules/bios/ldlinux.c32 /srv/tftp/

## Crie o link para o pxelinux.0 na raiz do tftp:
$› sudo ln -sf pxe/pxelinux.0 /srv/tftp/pxe5254

Agora vamos colocar na pasta da ISO que copiamos os arquivos necessários para fazer a instalação automática:

# Entre na pasta da ISO: 
$› cd /distros/ubuntu2004

# Crie a pasta para colocar os arquivos:
$› mkdir nocloud

# Crie um arquivo para o meta-data:
$› touch nocloud/meta-data

# Crie o user-data, use o exemplo na sessão 'USER-DATA':
$› vim nocloud/user-data

# Mude a permissão da pasta abaixo:
$› chmod -w -R nocloud/

# Mude a permissão do arquivo de hash:
$› chmod +w md5sum.txt

# Altere o hash de tudo que alteramos dentro dessta pasta:
$› find -follow -type f ! -name md5sum.txt -print0 | xargs -0 md5sum > md5sum.txt

# Deixe a permissão do arquivo de hash como estava:
$› chmod -w md5sum.txt


USER-DATA - Básico


Segue abaixo um modelo do arquivo user-data básico para uso.

#cloud-config
autoinstall:
version: 1
locale: en_US.UTF-8
keyboard:
layout: br
apt:
preserve_sources_list: false
sources_list: |
deb http://archive.ubuntu.com/ubuntu/ focal main universe
deb http://archive.ubuntu.com/ubuntu/ focal-updates main universe
deb http://archive.ubuntu.com/ubuntu/ focal-security main universe
deb http://archive.ubuntu.com/ubuntu/ focal-backports main universe
identity:
hostname: ubuntu
username: admin
password: $6$dzXL3m9SSOvAXlSX$vjYirzZijfB/Drse4Pgl79emtwrtSFhDoZDC.9j3h7bZcacyIsuX.KRN9BmZ7EApnmfv9ssjzP8WkBf7adxX4.
# Senha: 123
packages:
- vim
user-data:
disable_root: yes
final_message: "The system is finally up, after $UPTIME seconds"



USER-DATA - Básico + Repositório APT


Segue um exemplo adicionando uma chave GPG para um repositório.

#cloud-config
autoinstall:
version: 1
locale: en_US.UTF-8
keyboard:
layout: br
apt:
preserve_sources_list: false
sources_list: |
deb http://archive.ubuntu.com/ubuntu/ focal main universe
deb http://archive.ubuntu.com/ubuntu/ focal-updates main universe
deb http://archive.ubuntu.com/ubuntu/ focal-security main universe
deb http://archive.ubuntu.com/ubuntu/ focal-backports main universe
sources:
saltstack.list:
source: "deb http://archive.ubuntu.com/py3/ubuntu/20.04/amd64/latest focal main"
key: |
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

mQENBFOpvpgBCADkP656H41i8fpplEEB8IeLhugyC2rTEwwSclb8tQNYtUiGdna9
m38kb0OS2DDrEdtdQb2hWCnswxaAkUunb2qq18vd3dBvlnI+C4/xu5ksZZkRj+fW
tArNR18V+2jkwcG26m8AxIrT+m4M6/bgnSfHTBtT5adNfVcTHqiT1JtCbQcXmwVw
WbqS6v/LhcsBE//SHne4uBCK/GHxZHhQ5jz5h+3vWeV4gvxS3Xu6v1IlIpLDwUts
kT1DumfynYnnZmWTGc6SYyIFXTPJLtnoWDb9OBdWgZxXfHEcBsKGha+bXO+m2tHA
gNneN9i5f8oNxo5njrL8jkCckOpNpng18BKXABEBAAG0MlNhbHRTdGFjayBQYWNr
YWdpbmcgVGVhbSA8cGFja2FnaW5nQHNhbHRzdGFjay5jb20+iQE4BBMBAgAiBQJT
qb6YAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAOCKFJ3le/vhkqB/0Q
WzELZf4d87WApzolLG+zpsJKtt/ueXL1W1KA7JILhXB1uyvVORt8uA9FjmE083o1
yE66wCya7V8hjNn2lkLXboOUd1UTErlRg1GYbIt++VPscTxHxwpjDGxDB1/fiX2o
nK5SEpuj4IeIPJVE/uLNAwZyfX8DArLVJ5h8lknwiHlQLGlnOu9ulEAejwAKt9CU
4oYTszYM4xrbtjB/fR+mPnYh2fBoQO4d/NQiejIEyd9IEEMd/03AJQBuMux62tjA
/NwvQ9eqNgLw9NisFNHRWtP4jhAOsshv1WW+zPzu3ozoO+lLHixUIz7fqRk38q8Q
9oNR31KvrkSNrFbA3D89uQENBFOpvpgBCADJ79iH10AfAfpTBEQwa6vzUI3Eltqb
9aZ0xbZV8V/8pnuU7rqM7Z+nJgldibFk4gFG2bHCG1C5aEH/FmcOMvTKDhJSFQUx
uhgxttMArXm2c22OSy1hpsnVG68G32Nag/QFEJ++3hNnbyGZpHnPiYgej3FrerQJ
zv456wIsxRDMvJ1NZQB3twoCqwapC6FJE2hukSdWB5yCYpWlZJXBKzlYz/gwD/Fr
GL578WrLhKw3UvnJmlpqQaDKwmV2s7MsoZogC6wkHE92kGPG2GmoRD3ALjmCvN1E
PsIsQGnwpcXsRpYVCoW7e2nW4wUf7IkFZ94yOCmUq6WreWI4NggRcFC5ABEBAAGJ
AR8EGAECAAkFAlOpvpgCGwwACgkQDgihSd5Xv74/NggA08kEdBkiWWwJZUZEy7cK
WWcgjnRuOHd4rPeT+vQbOWGu6x4bxuVf9aTiYkf7ZjVF2lPn97EXOEGFWPZeZbH4
vdRFH9jMtP+rrLt6+3c9j0M8SIJYwBL1+CNpEC/BuHj/Ra/cmnG5ZNhYebm76h5f
T9iPW9fFww36FzFka4VPlvA4oB7ebBtquFg3sdQNU/MmTVV4jPFWXxh4oRDDR+8N
1bcPnbB11b5ary99F/mqr7RgQ+YFF0uKRE3SKa7a+6cIuHEZ7Za+zhPaQlzAOZlx
fuBmScum8uQTrEF5+Um5zkwC7EXTdH1co/+/V/fpOtxIg4XO4kcugZefVm5ERfVS
MA==
=dtMN
-----END PGP PUBLIC KEY BLOCK-----
identity:
hostname: ubuntu
username: admin
password: $6$PCEVa65wtyINGHXY$2pCsKii5D7M7UZRhtDMUlYrvhocgu/6AQf8dCNvuhezj8SWBsmc5INShr..KVSxcU3bBhmxgPPMAc5MOSkSbN.
ssh:
install-server: yes
packages:
- ubuntu-desktop
user-data:
disable_root: yes
final_message: "The system is finally up, after $UPTIME seconds"



USER-DATA - Básico + Repositório APT + SWAP + Disco com CONFIG


Exemplo contendo uma partição uma partição swap de 5GB e uma partição raiz / com o restante do disco (sem usar lvm).

#cloud-config
autoinstall:
version: 1
locale: en_US.UTF-8
keyboard:
layout: br
apt:
preserve_sources_list: false
sources_list: |
deb http://archive.ubuntu.com/ubuntu/ focal main universe
deb http://archive.ubuntu.com/ubuntu/ focal-updates main universe
deb http://archive.ubuntu.com/ubuntu/ focal-security main universe
deb http://archive.ubuntu.com/ubuntu/ focal-backports main universe
storage:
config:
# Configurando o disco que vamos usar:
- id: disk0
type: disk
ptable: msdos
name: main_disk
wipe: superblock
grub_device: true
#
# Criando uma partição para esse disco, sendo a primeira (1) para swap:
- id: disk0-swap
type: partition
number: 1
size: 5GB
device: disk0
flag: swap
#
# Criando uma partição para esse disco, sendo a segunda (2) para raoz '/':
- id: disk0-root
type: partition
number: 2
size: -1
device: disk0
#
# Formatando a partição swap:
- id: disk0-swap-format
type: format
fstype: swap
volume: disk0-swap
#
# Formatando a partição raiz:
- id: disk0-root-format
type: format
fstype: ext4
volume: disk0-root
#
# Montando a partição swap (path: none)
- id: disk0-swap-mount
type: mount
path: none
device: disk0-swap-format
#
# Montando a partição raiz em '/':
- id: disk0-root-mount
type: mount
path: /
device: disk0-root-format
identity:
hostname: ubuntu
username: admin
password: $6$dzXL3m9SSOvAXlSX$vjYirzZijfB/Drse4Pgl79emtwrtSFhDoZDC.9j3h7bZcacyIsuX.KRN9BmZ7EApnmfv9ssjzP8WkBf7adxX4.
packages:
- vim
user-data:
disable_root: yes
final_message: "The system is finally up, after $UPTIME seconds"


USER-DATA - Básico + Repositório APT + SWAP + Disco com CONFIG + GPT


Configurando disco com GPT em sistema apenas MBR.

#cloud-config
autoinstall:
version: 1
locale: en_US.UTF-8
keyboard:
layout: br
apt:
preserve_sources_list: false
sources_list: |
deb http://archive.ubuntu.com/ubuntu/ focal main universe
deb http://archive.ubuntu.com/ubuntu/ focal-updates main universe
deb http://archive.ubuntu.com/ubuntu/ focal-security main universe
deb http://archive.ubuntu.com/ubuntu/ focal-backports main universe
storage:
config:
# Configurando o disco que vamos usar:
- id: disk0
type: disk
ptable: gpt
name: main_disk
wipe: superblock
grub_device: true
#
# Resolvendo problema do gpt na MBR:
- id: disk0-mbr-partition
type: partition
number: 1
size: 1MB
device: disk0
flag: bios_grub
#
# Criando uma partição para esse disco, sendo a primeira (1) para swap:
- id: disk0-swap
type: partition
number: 2
size: 5GB
device: disk0
flag: swap
#
# Criando uma partição para esse disco, sendo a segunda (2) para raoz '/':
- id: disk0-root
type: partition
number: 3
size: -1
device: disk0
#
# Formatando a partição swap:
- id: disk0-swap-format
type: format
fstype: swap
volume: disk0-swap
#
# Formatando a partição raiz:
- id: disk0-root-format
type: format
fstype: ext4
volume: disk0-root
#
# Montando a partição swap (path: none)
- id: disk0-swap-mount
type: mount
path: none
device: disk0-swap-format
#
# Montando a partição raiz em '/':
- id: disk0-root-mount
type: mount
path: /
device: disk0-root-format
identity:
hostname: ubuntu
username: admin
password: $6$dzXL3m9SSOvAXlSX$vjYirzZijfB/Drse4Pgl79emtwrtSFhDoZDC.9j3h7bZcacyIsuX.KRN9BmZ7EApnmfv9ssjzP8WkBf7adxX4.
packages:
- vim
user-data:
disable_root: yes
final_message: "The system is finally up, after $UPTIME seconds"


USER-DATA - Básico + UEFI


A execução do user-data no formato abaixo, notei uma melhora no desempenho em sistemas UEFI. Aqui temos um disco em gpt, uma partição swap, uma /boot/efi e a raiz /.

#cloud-config
autoinstall:
version: 1
locale: en_US.UTF-8
keyboard:
layout: br
storage:
grub:
reorder_uefi: False
config:
# Declara o disco:
- {id: disk0, type: disk, ptable: gpt, name: main_disk, wipe: superblock}
# Declara o /boot/efi e formata:
- {id: disk0-boot-efi, type: partition, size: 3GB, device: disk0, number: 1, flag: boot, grub_device: true}
- {id: disk0-boot-efi-format, type: format, fstype: fat32, volume: disk0-boot-efi, preserve: false, flag: boot}
# Declara o swap e formata:
- {id: disk0-swap, type: partition, size: 5GB, device: disk0, number: 2, preserve: false}
- {id: disk0-swap-format, type: format, fstype: swap, volume: disk0-swap, preserve: false}
# Declara a raiz '/' e formata:
- {id: disk0-root, type: partition, size: -1, number: 3, device: disk0}
- {id: disk0-root-format, type: format, fstype: ext4, volume: disk0-root}
# Monta as particoes:
- {id: disk0-swap-mount, type: mount, path: none, device: disk0-swap-format}
- {id: disk0-boot-efi-mount, type: mount, path: /boot/efi, device: disk0-boot-efi-format}
- {id: disk0-root-mount, type: mount, path: /, device: disk0-root-format}
identity: {hostname: ubuntu, password: $6$hF3dRy6uWwNZ4QfW$kmWQ/K4DE3yeDa9zZo0XKZHJI6lU0GPRx9UOZDYuegjdncJjzFC40wbPmyP1CGtDWDx6/1GpixHqvrvxB.kwx0, username: admin}
apt:
preserve_sources_list: false
sources_list: |
deb http://IP/ubuntu/ focal main universe
deb http://IP/ubuntu/ focal-updates main universe
deb http://IP/ubuntu/ focal-security main universe
deb http://IP/ubuntu/ focal-backports main universe
sources:
saltstack.list:
source: "deb http://IP/py3/ubuntu/20.04/amd64/latest focal main"
key: |
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

mQENBFOpvpgBCADkP656H41i8fpplEEB8IeLhugyC2rTEwwSclb8tQNYtUiGdna9
m38kb0OS2DDrEdtdQb2hWCnswxaAkUunb2qq18vd3dBvlnI+C4/xu5ksZZkRj+fW
tArNR18V+2jkwcG26m8AxIrT+m4M6/bgnSfHTBtT5adNfVcTHqiT1JtCbQcXmwVw
WbqS6v/LhcsBE//SHne4uBCK/GHxZHhQ5jz5h+3vWeV4gvxS3Xu6v1IlIpLDwUts
kT1DumfynYnnZmWTGc6SYyIFXTPJLtnoWDb9OBdWgZxXfHEcBsKGha+bXO+m2tHA
gNneN9i5f8oNxo5njrL8jkCckOpNpng18BKXABEBAAG0MlNhbHRTdGFjayBQYWNr
YWdpbmcgVGVhbSA8cGFja2FnaW5nQHNhbHRzdGFjay5jb20+iQE4BBMBAgAiBQJT
qb6YAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAOCKFJ3le/vhkqB/0Q
WzELZf4d87WApzolLG+zpsJKtt/ueXL1W1KA7JILhXB1uyvVORt8uA9FjmE083o1
yE66wCya7V8hjNn2lkLXboOUd1UTErlRg1GYbIt++VPscTxHxwpjDGxDB1/fiX2o
nK5SEpuj4IeIPJVE/uLNAwZyfX8DArLVJ5h8lknwiHlQLGlnOu9ulEAejwAKt9CU
4oYTszYM4xrbtjB/fR+mPnYh2fBoQO4d/NQiejIEyd9IEEMd/03AJQBuMux62tjA
/NwvQ9eqNgLw9NisFNHRWtP4jhAOsshv1WW+zPzu3ozoO+lLHixUIz7fqRk38q8Q
9oNR31KvrkSNrFbA3D89uQENBFOpvpgBCADJ79iH10AfAfpTBEQwa6vzUI3Eltqb
9aZ0xbZV8V/8pnuU7rqM7Z+nJgldibFk4gFG2bHCG1C5aEH/FmcOMvTKDhJSFQUx
uhgxttMArXm2c22OSy1hpsnVG68G32Nag/QFEJ++3hNnbyGZpHnPiYgej3FrerQJ
zv456wIsxRDMvJ1NZQB3twoCqwapC6FJE2hukSdWB5yCYpWlZJXBKzlYz/gwD/Fr
GL578WrLhKw3UvnJmlpqQaDKwmV2s7MsoZogC6wkHE92kGPG2GmoRD3ALjmCvN1E
PsIsQGnwpcXsRpYVCoW7e2nW4wUf7IkFZ94yOCmUq6WreWI4NggRcFC5ABEBAAGJ
AR8EGAECAAkFAlOpvpgCGwwACgkQDgihSd5Xv74/NggA08kEdBkiWWwJZUZEy7cK
WWcgjnRuOHd4rPeT+vQbOWGu6x4bxuVf9aTiYkf7ZjVF2lPn97EXOEGFWPZeZbH4
vdRFH9jMtP+rrLt6+3c9j0M8SIJYwBL1+CNpEC/BuHj/Ra/cmnG5ZNhYebm76h5f
T9iPW9fFww36FzFka4VPlvA4oB7ebBtquFg3sdQNU/MmTVV4jPFWXxh4oRDDR+8N
1bcPnbB11b5ary99F/mqr7RgQ+YFF0uKRE3SKa7a+6cIuHEZ7Za+zhPaQlzAOZlx
fuBmScum8uQTrEF5+Um5zkwC7EXTdH1co/+/V/fpOtxIg4XO4kcugZefVm5ERfVS
MA==
=dtMN
-----END PGP PUBLIC KEY BLOCK-----
ssh:
install-server: true
packages:
- ubuntu-desktop
version: 1


Fornecendo um USER-DATA do Cloud-Init via Auto Install


Durante o AutoInstall, adicione um comando no bloco late-commands para definir o datasource padrão do Cloud-Init no sistema instalado, apontando para o seu servidor HTTP.


Assim, quando o novo sistema der boot pela primeira vez, o Cloud-Init vai buscar o user-data remoto automaticamente.

late-commands:
# Configura o Cloud-Init do sistema instalado para usar o datasource de rede
- curtin in-target --target=/target -- mkdir -p /etc/cloud/cloud.cfg.d
- curtin in-target --target=/target -- bash -c "echo 'datasource_list: [ NoCloud, None ]' > /etc/cloud/cloud.cfg.d/90_datasource.cfg"
- curtin in-target --target=/target -- bash -c \"echo 'datasource:\n NoCloud:\n seedfrom: http://192.168.1.15/nocloud/ubuntu2204-cloudinit/' > /etc/cloud/cloud.cfg.d/91_nocloud_net.cfg\"


Usando o Cloud Init sem AutoInstall


Primeiro devemos instalar o pacote cloud-init. O arquivo de configuação principal é o /etc/cloud/cloud.cfg, mas podemos colocar em /etc/cloud/cloud.cfg.d/.


Os módulos são executados em ordem dentro de três fases que são:

  1. A fase de inicialização cloud-init;
  2. A fase de configuração
  3. A fase final.

Dentro do arquivo cloud.cfg, os módulos para as três fases estão listados em cloud_init_modules, cloud_config_modules, e cloud_final_modules, respectivamente. Cada fase carrega módulos específicos.


O Cloud Init vem com alguns módulos que podemos usar, mas nem todos estão disponíveis no Auto Install do Ubuntu. No entanto, podemos usar o AutoInstall para instalar o pacote cloud-init (caso não esteja incluído na imagem) e configurar seus parâmetros básicos durante a instalação.


Depois que o sistema for instalado, o Cloud-Init assumirá o papel de configurar o sistema nos próximos boots (ex: pós-instalação, integração com nuvem, scripts automáticos, etc.).



Funcionamento em Estágios do cloud-init


O cloud-init executa o processo de inicialização em cinco estágios distintos durante o boot do sistema. Cada um desses estágios tem um papel específico, como decidir se vai rodar o cloud-init, descobrir de onde vieram os dados de configuração ("datasource"), aplicar rede, configurar o sistema e, por fim, rodar tarefas finais.


  1. Generator stage Aqui o sistema (via systemd) verifica se o cloud-init deve ser executado neste boot. Ou seja, decide se o cloud-init vai ou não rodar.

  2. Local stage No estágio local o cloud-init busca por fontes locais de dados (datasources) e já aplica certas configurações de rede, antes ou logo após a rede estar disponível.

  3. Network stage Depois que a rede está pronta, o cloud-init lê os user-data, metadata e possivelmente vendor-data, e executa os módulos listados em cloud_init_modules. É aqui que grande parte da configuração "automática" acontece.

  4. Config stage Neste momento são executados os módulos de configuração listados em cloud_config_modules. Aqui entram tarefas como instalar pacotes, aplicar configurações de usuários, montagem de discos, etc.

  5. Final stage Por fim, são executados os módulos de finalização (cloud_final_modules), que podem incluir scripts personalizados, execução de plug-ins de gestão, e qualquer tarefa pós-instalação que precise ser feita antes o sistema "entrar em produção".



Frequência de execução dos módulos


Os módulos do cloud-init têm frequências diferentes de execução, dependendo da configuração ou de eventos como clonagem de instância. Por exemplo:

  • per-instance: módulo que roda no primeiro boot da instância.
  • per-once: módulo que roda somente uma vez e não repete mesmo que se clone a instância.
  • per-always: módulo que roda a cada boot.


Dados que o cloud-init processa


O cloud-init age sobre três tipos de dados principais:

  • User data: as instruções que você define (por exemplo, no user-data).
  • Metadata: informações da instância, como instance-id, hostname, etc.
  • Vendor data: dados opcionais fornecidos pela "nuvem" ou provedor, para customizar a imagem ou instância.


Módulos


O Cloud-Init é dividido internamente em módulos, e cada módulo executa uma tarefa específica no processo de inicialização, como configurar rede, criar usuários, instalar pacotes, gravar arquivos ou executar comandos.


Esses módulos são agrupados em três listas principais, que determinam a ordem e o momento em que cada um será executado (como já vimos).


Os módulos padrão (e sua ordem de execução) são definidos no arquivo principal (/etc/cloud/cloud.cfg). Os módulos propriamente ditos (os scripts em Python que fazem as ações) ficam em /usr/lib/python3/dist-packages/cloudinit/config/.


Nós podemos alterar a ordem de execução, desabilitar ou adicionar módulos sem editar o arquivo principal (/etc/cloud/cloud.cfg) diretamente, basta criar arquivos adicionais dentro de:

/etc/cloud/cloud.cfg.d/

Por exemplo:

# /etc/cloud/cloud.cfg.d/90_custom_modules.cfg
cloud_final_modules:
- scripts-per-boot
- runcmd
- final-message

O Cloud-Init lê os arquivos em ordem crescente, ou seja, um arquivo 90_custom_modules.cfg sobrescreve o que foi definido em 50_cloud_init.cfg.


Os arquivos dentro de /usr/share/doc/cloud-init/examples/ são arquivos de exemplos do user-data (formato #cloud-config) distribuído junto com o Cloud-Init. Eles mostram como podemos automatizar toda a configuração do módulo.



Frequência de execução dos módulos


Os módulos têm diferentes comportamentos definidos pelo parâmetro frequency, que pode ser:

ValorDescrição
per-instanceExecuta no primeiro boot da instância (e novamente se o instance-id mudar).
onceExecuta apenas uma vez, independentemente de reinstâncias ou clones.
alwaysExecuta em todo boot.

Essa configuração fica no topo de cada módulo Python. Exemplo (extraído do código-fonte do cc_runcmd.py):

frequency = PER_INSTANCE

O Cloud-Init grava o estado da última execução em /var/lib/cloud/. Enquanto esse diretório existir, ele entende que a máquina já foi inicializada e não roda novamente, por isso precisamos "limpar" esse estado.



Limpar o estado e forçar nova execução

Este é o método mais direto:

sudo cloud-init clean --logs --reboot

O que faz:

  • --logs: remove os logs antigos;
  • --reboot: reinicia o sistema após a limpeza.

Ao reiniciar, o Cloud-Init se comporta como se fosse o primeiro boot, buscando novamente o datasource (NoCloud, EC2, etc.) e executando todos os módulos (init, config, final).



Forçar apenas uma fase específica

Você pode executar apenas uma das etapas do processo, sem reiniciar:

sudo cloud-init init --local      # Fase local (antes da rede)
sudo cloud-init init # Fase de inicialização (init)
sudo cloud-init modules --mode=config # Fase de configuração
sudo cloud-init modules --mode=final # Fase final

Isso é útil quando você quer testar um trecho de user-data sem refazer tudo.



Reexecutar módulos específicos

Também dá pra rodar um único módulo manualmente:

sudo cloud-init single --name write-files
sudo cloud-init single --name runcmd

Excelente pra depurar o comportamento de um módulo isolado.



Verificar status da execução

Antes de forçar a execução, veja o estado atual:

cloud-init status --long

Saída típica:

status: done
time: Thu, 27 Oct 2025 13:00:00 +0000
detail:
DataSource: DataSourceNoCloud [seed=/dev/sr0][dsmode=net]

Se aparecer done, significa que ele já concluiu e não rodará novamente até ser limpo.



Apagar manualmente o estado (modo bruto)

Se quiser limpar tudo manualmente (sem reboot):

sudo rm -rf /var/lib/cloud/
sudo rm -rf /var/log/cloud-init*.log
sudo systemctl restart cloud-init-local.service cloud-init.service cloud-config.service cloud-final.service

Isso apaga completamente o histórico e força o Cloud-Init a reiniciar o ciclo no próximo boot.


Dica bônus

Durante o desenvolvimento de imagens (ou testes com QEMU/NoCloud), é comum usar este ciclo:

sudo cloud-init clean --reboot
cloud-init status --wait

Assim, você garante que o Cloud-Init vai:

  1. Detectar novamente o datasource;
  2. Ler o novo user-data e meta-data;
  3. Reexecutar todos os módulos como se fosse a primeira inicialização.


Criar um cloud-init para testes


Vamos criar uma configuração user-data simples e executar ela manualmente via datasource NoCloud. Depois vamos ver o log e o resultado real.


Crie um diretório de teste:

sudo mkdir -p /var/lib/cloud/seed/nocloud
cd /var/lib/cloud/seed/nocloud

Crie o arquivo user-data:

cat << 'EOF' > user-data
#cloud-config
hostname: cloudtest
timezone: America/Sao_Paulo
locale: pt_BR.UTF-8

users:
- name: bruno
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh_authorized_keys:
- ssh-ed25519 AAAAB3Nz...sua_chave_publica...

package_update: true
packages:
- htop
- curl

write_files:
- path: /etc/motd
permissions: '0644'
content: |
========================================
Servidor configurado via Cloud-Init local
Hostname: cloudtest
========================================

runcmd:
- echo "Executando comando de teste via Cloud-Init..." >> /root/cloudinit_test.log
- systemctl enable ssh
- systemctl restart ssh
EOF

Esse user-data vai:

  • definir hostname, timezone e locale;
  • criar o usuário bruno;
  • instalar htop e curl;
  • alterar o /etc/motd;
  • e rodar comandos simples.

Crie o arquivo meta-data:

cat << EOF > meta-data
instance-id: cloudinit-manual-001
local-hostname: cloudtest
EOF

Limpe o estado anterior, isso garante que o Cloud-Init rode do zero:

sudo cloud-init clean --logs
sudo rm -rf /var/lib/cloud/instance

Execute as fases na ordem:

sudo cloud-init init --local
sudo cloud-init init
sudo cloud-init modules --mode=config
sudo cloud-init modules --mode=final

Também poderiamos simplificar tudo com apenas usando o comando sudo cloud-init clean --reboot, mas não iríamos ver o passo a passo sem reboot.


user-data diferente do Auto install

O user-data do Cloud-Init é diferente do user-data do AutoInstall, embora ambos usem YAML e o Cloud-Init esteja por baixo dos dois. Eles estão relacionados, mas servem a propósitos diferentes e têm estruturas distintas.


O Cloud-Init (#cloud-config) é um arquivo YAML puro com instruções de configuração de sistema. Ele é executado no primeiro boot do sistema já instalado. Já o AutoInstall (autoinstall:) é um arquivo YAML estendido, que usa o Cloud-Init como backend, mas voltado à instalação. Ele é executado durante a instalação do Ubuntu (fase do Subiquity).


Quando você faz uma instalação automática do Ubuntu, o AutoInstall usa um arquivo com esta estrutura:

#cloud-config
autoinstall:
version: 1
identity:
hostname: servidor
username: bruno
password: "$6$hash..."
packages:
- vim
- htop
storage:
layout:
name: lvm
ssh:
install-server: true

Já o user-data do Cloud-Init não tem a chave autoinstall:. Ele é executado no sistema já instalado, e o conteúdo é aplicado diretamente pelo Cloud-Init.

Exemplo:

#cloud-config
hostname: lab-test
timezone: America/Sao_Paulo

users:
- name: bruno
groups: sudo
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']

packages:
- nginx
- net-tools

write_files:
- path: /etc/motd
content: |
Bem-vindo ao servidor configurado via Cloud-Init!

runcmd:
- systemctl enable nginx
- systemctl start nginx


Verifique se funcionou


Ao final de todos os comandos acima, vamos ver se funcionou a execução do cloud init usando o user-data.

# Verifique o hostname:
hostnamectl

# Verifique se criou o usuário:
grep bruno /etc/passwd

# Verifique se o motd foi modificado:
cat /etc/motd

# Verifique os comandos executados:
cat /root/cloudinit_test.log

# Também olhe os logs do Cloud-Init:
sudo tail -n 20 /var/log/cloud-init.log
sudo tail -n 20 /var/log/cloud-init-output.log

# Verifique o datasource detectado:
cloud-init query ds

Dessa forma, quando a gente força a execução do Cloud-Init manualmente com um datasource local (NoCloud), somente o que está definido em user-data (e parte das informações complementares do meta-data) será aplicado, nada mais.


O Cloud-Init vai ler o user-data e o meta-data da fonte que você especificou (no nosso caso, /var/lib/cloud/seed/nocloud/). O que está no user-data, ou seja, no seu arquivo começando com #cloud-config é o conjunto de instruções que o Cloud-Init entende e executa usando seus módulos internos.



Fontes


https://discourse.ubuntu.com/t/issue-with-curtin-while-trying-to-autoinstall-ubuntu-20-04-focal/20046

https://askubuntu.com/questions/1235723/automated-20-04-server-installation-using-pxe-and-live-server-image

https://askubuntu.com/questions/1296787/curtin-error-on-autoinstall-ubuntu-20-04-1

https://ubuntu.com/server/docs/install/autoinstall-reference

https://askubuntu.com/questions/1233454/how-to-preseed-ubuntu-20-04-desktop

https://cloud-init.io/

https://tlhakhan.medium.com/ubuntu-server-20-04-autoinstall-2e5f772b655a

https://ubuntu.com/server/docs/install/autoinstall-quickstart

https://ubuntu.com/server/docs/install/autoinstall

https://cloudinit.readthedocs.io/en/latest/topics/datasources/nocloud.html

https://www.lucd.info/2019/12/07/cloud-init-part-2-advanced-ubuntu/

https://cloudinit.readthedocs.io/en/0.7.7/topics/examples.html#add-apt-repositories

https://wiki.archlinux.org/title/Cloud-init

https://nickcharlton.net/posts/automating-ubuntu-2004-installs-with-packer.html

https://cloudinit.readthedocs.io/en/latest/topics/debugging.html

http://cdimage.ubuntu.com/ubuntu-server/focal/daily-live/current/

https://ubuntu.com/server/docs/install/autoinstall-schema

https://cloudinit.readthedocs.io/en/latest/topics/examples.html

https://curtin.readthedocs.io/en/latest/topics/storage.html

https://www.golinuxcloud.com/generate-user-data-file-ubuntu-20-04/

https://docs.redhat.com/pt-br/documentation/red_hat_enterprise_linux/8/html/configuring_and_managing_cloud-init_for_rhel_8/cloud-init-operates-in-stages_introduction-to-cloud-init

Boa fonte para UEFI:

https://www.golinuxcloud.com/uefi-pxe-boot-server-ubuntu-20-04-cloud-init/

https://wiki.ubuntu.com/UEFI/PXE-netboot-install

https://askubuntu.com/questions/1359429/ubuntu-20-04-2-unable-to-run-the-scripts-post-installation-as-a-late-command

https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/configuring_and_managing_cloud-init_for_rhel_8/introduction-to-cloud-init_cloud-content

https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/pdf/configuring_and_managing_cloud-init_for_rhel_8/Red_Hat_Enterprise_Linux-8-Configuring_and_managing_cloud-init_for_RHEL_8-en-US.pdf

https://documentation.ubuntu.com/lxd/latest/cloud-init/

https://cloudinit.readthedocs.io/en/latest/tutorial/index.html