Criação de Template Oracle Linux 9 QEMU/KVM com Packer
Introdução
A criação de máquinas virtuais é uma tarefa fundamental em ambientes de TI modernos, seja para desenvolvimento, testes ou produção. No entanto, configurar manualmente cada máquina virtual pode ser um processo demorado e propenso a erros. É nesse contexto que ferramentas de automação como o Packer se tornam essenciais, permitindo a criação de templates de máquinas virtuais de forma padronizada, reproduzível e eficiente.
O Oracle Linux, uma distribuição empresarial baseada no Red Hat Enterprise Linux (RHEL), oferece estabilidade, segurança e suporte de longo prazo, tornando-o uma escolha excelente para ambientes corporativos. A versão 9, em particular, traz melhorias significativas em termos de desempenho, segurança e compatibilidade com aplicações modernas. Combinar o Oracle Linux 9 com ferramentas de automação como o Packer permite criar uma infraestrutura robusta e facilmente gerenciável.
O Packer, desenvolvido pela HashiCorp, é uma ferramenta de código aberto que automatiza a criação de imagens de máquinas virtuais para múltiplas plataformas a partir de uma única configuração. Com ele, podemos definir todo o processo de instalação e configuração em arquivos de código, seguindo o princípio de Infraestrutura como Código (IaC). Isso não apenas economiza tempo, mas também garante consistência entre ambientes e facilita a documentação e o versionamento das configurações.
Neste tutorial, vamos explorar como criar um template do Oracle Linux 9 para uso com QEMU/KVM, uma das tecnologias de virtualização mais populares em ambientes Linux. Vamos utilizar o Packer para automatizar todo o processo de instalação e configuração, resultando em uma imagem pronta para uso que pode ser facilmente replicada e personalizada conforme necessário. Ao final, também veremos como testar nossa imagem utilizando o Terraform, outra ferramenta poderosa da HashiCorp para gerenciamento de infraestrutura.
Pré-requisitos
Antes de iniciarmos o processo de criação do template, é importante garantir que você tenha o ambiente adequado e os conhecimentos básicos necessários. Para seguir este tutorial, você precisará de:
- Um sistema Linux (utilizaremos Ubuntu como exemplo, mas os conceitos se aplicam a outras distribuições)
- Acesso de superusuário (root) ou permissões sudo
- Conhecimentos básicos de linha de comando Linux
- Familiaridade com conceitos de virtualização
- Conexão com a internet para download de pacotes e imagens
As ferramentas que utilizaremos incluem:
- QEMU/KVM: como plataforma de virtualização
- Libvirt: para gerenciamento de máquinas virtuais
- Packer: para automação da criação de imagens
- Terraform: para testar o template criado
- Cloud-init: para configuração inicial da máquina virtual
Configurando o KVM no Ubuntu
O KVM (Kernel-based Virtual Machine) é uma tecnologia de virtualização integrada ao kernel Linux que permite executar máquinas virtuais com desempenho próximo ao nativo. O Libvirt é uma API e um conjunto de ferramentas para gerenciar plataformas de virtualização, incluindo o KVM. Vamos configurar esses componentes para criar um ambiente robusto para nossas máquinas virtuais.
Instalando os Pacotes Necessários
Primeiro, vamos instalar os pacotes essenciais para trabalhar com KVM e gerenciar máquinas virtuais através de uma interface gráfica (Virt-Manager) e linha de comando:
1
sudo apt install virt-manager libosinfo-bin mkisofs -y
Neste comando, estamos instalando:
virt-manager
: Uma interface gráfica para gerenciar máquinas virtuaislibosinfo-bin
: Biblioteca que fornece informações sobre sistemas operacionais para ferramentas de virtualizaçãomkisofs
: Ferramenta para criar imagens ISO, útil para configurações personalizadas
Para permitir que seu usuário gerencie máquinas virtuais sem precisar de privilégios de root, adicione-o ao grupo kvm
:
1
sudo usermod -aG kvm $USER
Esta alteração de grupo requer um reinício do sistema para ser aplicada corretamente:
1
sudo systemctl reboot
Criando Diretórios para Armazenamento
Uma boa organização dos arquivos de máquinas virtuais facilita o gerenciamento e a manutenção. Vamos criar uma estrutura de diretórios dedicada:
1
2
3
mkdir -p ~/kvm/images
mkdir -p ~/kvm/templates
mkdir -p ~/kvm/isos
Esta estrutura inclui:
images
: Diretório principal onde as máquinas virtuais em uso serão armazenadastemplates
: Diretório para armazenar imagens base pré-configuradasisos
: Diretório para armazenar arquivos ISO de instalação de sistemas operacionais
Configurando Pools de Armazenamento
O Libvirt utiliza o conceito de “pools de armazenamento” para gerenciar onde os discos virtuais são armazenados. Vamos criar três pools correspondentes aos diretórios que acabamos de criar:
1
2
3
virsh pool-define-as --name default --type dir --target ~/kvm/images
virsh pool-define-as --name templates --type dir --target ~/kvm/templates
virsh pool-define-as --name isos --type dir --target ~/kvm/isos
Cada comando cria um pool de armazenamento com um nome específico, do tipo diretório, apontando para o caminho que definimos anteriormente.
Para garantir que esses pools sejam iniciados automaticamente quando o sistema for ligado:
1
2
3
virsh pool-autostart default
virsh pool-autostart templates
virsh pool-autostart isos
E para iniciar os pools imediatamente, sem precisar reiniciar o sistema:
1
2
3
virsh pool-start default
virsh pool-start templates
virsh pool-start isos
Para verificar se os pools foram criados e iniciados corretamente:
1
2
virsh pool-list --all
virsh pool-info default
O primeiro comando lista todos os pools disponíveis, enquanto o segundo mostra informações detalhadas sobre o pool “default”.
Ajustando Permissões no AppArmor
O AppArmor é um sistema de segurança presente em distribuições como o Ubuntu que pode restringir o acesso a determinados recursos. Para evitar problemas de permissão ao acessar os diretórios de armazenamento das VMs, vamos criar um perfil personalizado:
1
2
3
4
5
6
7
8
9
sudo tee /etc/apparmor.d/libvirt/TEMPLATE.qemu > /dev/null <<EOF
#include <tunables/global>
profile LIBVIRT_TEMPLATE flags=(attach_disconnected) {
#include <abstractions/libvirt-qemu>
/home/$USER/kvm/images/** rwk,
/home/$USER/kvm/templates/** rwk,
}
EOF
Este perfil permite que o QEMU (usado pelo KVM) acesse os diretórios que criamos com permissões de leitura, escrita e criação de links (rwk).
Agora, precisamos verificar qual usuário e grupo o Libvirt utiliza para executar as máquinas virtuais:
1
sudo grep -E '#user|#group' /etc/libvirt/qemu.conf
Se a saída mostrar algo como:
1
2
#user = "libvirt-qemu"
#group = "kvm"
Então, devemos ajustar as permissões dos diretórios para esse usuário e grupo:
1
sudo chown libvirt-qemu:kvm ~/kvm/ -R
Este comando altera o proprietário e o grupo de todos os arquivos e diretórios dentro de ~/kvm/ para libvirt-qemu:kvm, permitindo que o Libvirt acesse esses recursos.
Finalmente, recarregamos as regras do AppArmor e reiniciamos o serviço Libvirt:
1
2
sudo apparmor_parser -r /etc/apparmor.d/libvirt/TEMPLATE.qemu
sudo systemctl restart libvirtd
Para verificar se há erros relacionados ao AppArmor:
1
sudo journalctl -xe | grep apparmor
Se não houver mensagens de erro, a configuração foi bem-sucedida.
Instalação e Configuração do Terraform
O Terraform é uma ferramenta de infraestrutura como código (IaC) que permite definir e provisionar infraestrutura de forma declarativa. Vamos instalá-lo e configurá-lo para trabalhar com o Libvirt, o que nos permitirá criar e gerenciar máquinas virtuais de forma automatizada.
Baixando e Instalando a Última Versão
Primeiro, vamos obter a versão mais recente do Terraform diretamente do repositório oficial:
1
TER_VER=$(curl -s https://api.github.com/repos/hashicorp/terraform/releases/latest | grep tag_name | cut -d: -f2 | tr -d \"\,\v | awk '{$1=$1};1')
Este comando consulta a API do GitHub para obter a tag da versão mais recente do Terraform e a armazena na variável TER_VER
.
Agora, vamos baixar o binário, descompactá-lo e instalá-lo:
1
2
3
4
wget https://releases.hashicorp.com/terraform/${TER_VER}/terraform_${TER_VER}_linux_amd64.zip
unzip terraform_${TER_VER}_linux_amd64.zip
sudo mv terraform /usr/local/bin/
rm terraform_${TER_VER}_linux_amd64.zip
Estes comandos:
- Baixam o arquivo ZIP da versão mais recente do Terraform
- Descompactam o arquivo
- Movem o binário para o diretório /usr/local/bin/ (que geralmente está no PATH)
- Removem o arquivo ZIP, que não é mais necessário
Para verificar se a instalação foi bem-sucedida:
1
2
which terraform
terraform --version
O primeiro comando deve mostrar o caminho para o binário do Terraform (/usr/local/bin/terraform), e o segundo deve exibir a versão instalada.
Configurando o Ambiente para o Provider Libvirt
O Terraform utiliza “providers” para interagir com diferentes plataformas. Para trabalhar com o KVM/Libvirt, precisamos configurar o ambiente adequadamente.
Primeiro, vamos definir a variável de ambiente que especifica o URI padrão para conexão com o Libvirt. Adicione a seguinte linha ao seu arquivo ~/.bashrc:
1
export LIBVIRT_DEFAULT_URI="qemu:///system"
Você pode fazer isso manualmente ou usando o seguinte comando:
1
2
echo 'export LIBVIRT_DEFAULT_URI="qemu:///system"' >> ~/.bashrc
source ~/.bashrc
Esta configuração indica ao Terraform que deve se conectar ao daemon Libvirt local com privilégios de sistema.
Criando uma Chave SSH para Acesso às VMs
Para acessar as máquinas virtuais que criaremos com o Terraform, é útil configurar uma chave SSH. Vamos criar um par de chaves dedicado para este propósito:
1
ssh-keygen -t rsa -b 4096 -f ~/.ssh/tfvms
Este comando cria:
- Uma chave privada em ~/.ssh/tfvms
- Uma chave pública em ~/.ssh/tfvms.pub
A chave pública será utilizada pelo cloud-init para configurar o acesso SSH às máquinas virtuais, permitindo que você se conecte a elas sem precisar digitar uma senha.
Instalação do Packer no Ubuntu
O Packer é a ferramenta central que utilizaremos para criar nossos templates de máquinas virtuais. Vamos instalá-lo a partir do repositório oficial da HashiCorp.
Primeiro, adicionamos a chave GPG do repositório:
1
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
Em seguida, adicionamos o repositório ao sistema:
1
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
O comando lsb_release -cs
retorna o nome da versão da sua distribuição Ubuntu (como “focal” para Ubuntu 20.04 ou “jammy” para Ubuntu 22.04), garantindo que você adicione o repositório correto para sua versão específica.
Agora, atualizamos a lista de pacotes e instalamos o Packer:
1
sudo apt-get update && sudo apt-get install packer
Para verificar se a instalação foi bem-sucedida:
1
packer version
Você deverá ver a versão do Packer instalada em seu sistema. Para este tutorial, recomendamos utilizar a versão 1.10.0 ou superior, conforme especificado em nossos templates.
Preparação do Ambiente de Trabalho
Com todas as ferramentas necessárias instaladas, vamos criar a estrutura de diretórios para nosso projeto Packer. Uma boa organização é fundamental para manter o trabalho limpo e facilitar futuras modificações ou expansões.
Vamos criar um diretório para nossos projetos Packer, com subdiretórios específicos para o tipo de virtualização (KVM) e a distribuição (Oracle Linux 9):
1
2
mkdir -p ~/packer/kvm/ol9
cd ~/packer/kvm/ol9
Esta estrutura nos permite organizar diferentes projetos de forma lógica. Se no futuro quisermos criar templates para outras distribuições ou plataformas de virtualização, podemos simplesmente adicionar novos diretórios mantendo a mesma estrutura organizacional.
Dentro do diretório ol9
, criaremos todos os arquivos necessários para nosso projeto: o template principal do Packer, o script de limpeza e o arquivo kickstart para automação da instalação do Oracle Linux 9.
Criação do Template Packer
O coração do nosso projeto é o arquivo de template do Packer, que define todo o processo de criação da imagem. Vamos criar um arquivo chamado os-install.pkr.hcl
usando seu editor de texto preferido:
1
vim os-install.pkr.hcl
Agora, vamos analisar o conteúdo deste arquivo em detalhes:
1
2
3
4
5
6
7
8
9
packer {
required_version = ">= 1.10.0"
required_plugins {
qemu = {
version = "= 1.1.0"
source = "github.com/hashicorp/qemu"
}
}
}
Esta primeira seção define os requisitos do Packer. Especificamos que precisamos da versão 1.10.0 ou superior do Packer e do plugin QEMU na versão exata 1.1.0. O plugin QEMU é necessário para interagir com o QEMU/KVM durante o processo de build.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
variable "vm_name" {
default = "ol9-kvm-template.raw"
}
variable "disk_size" {
default = "16384"
}
variable "iso_url" {
default = "https://yum.oracle.com/ISOS/OracleLinux/OL9/u4/x86_64/OracleLinux-R9-U4-x86_64-boot.iso"
}
variable "iso_checksum" {
default = "975de11be8761efa4aa2c87d7d3bedcb62c9dc956909b68f62c99062d11599e9"
}
Nesta seção, definimos variáveis que serão utilizadas ao longo do template. Isso facilita a manutenção e personalização do processo de build. As variáveis incluem:
vm_name
: Nome do arquivo de imagem que será geradodisk_size
: Tamanho do disco virtual em MB (16GB neste caso, maior que o Debian devido às necessidades do Oracle Linux)iso_url
: URL para download da ISO do Oracle Linux 9 Update 4iso_checksum
: Checksum SHA256 da ISO para verificação de integridade
É importante sempre verificar o checksum da ISO para garantir que estamos utilizando uma imagem íntegra e oficial. Você pode encontrar o checksum mais recente no site oficial da Oracle.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
source "qemu" "iso" {
vm_name = var.vm_name
iso_url = var.iso_url
iso_checksum = var.iso_checksum
disk_size = var.disk_size
memory = 2048
disk_image = false
output_directory = "build/os-base"
accelerator = "kvm"
disk_interface = "virtio"
format = "raw"
net_device = "virtio-net"
boot_wait = "3s"
boot_command = [
"<up>",
"<tab><wait>",
" inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/ks.cfg",
"<enter>"
]
http_directory = "http"
cpu_model = "host"
shutdown_command = "echo 'packer' | sudo -S shutdown -P now"
ssh_username = "packer"
ssh_password = "packer"
ssh_timeout = "60m"
}
Esta seção define a fonte (source) para o build, que é a configuração da máquina virtual QEMU que será utilizada. Vamos analisar os principais parâmetros:
vm_name
,iso_url
,iso_checksum
edisk_size
: Utilizam as variáveis definidas anteriormentememory
: Define a quantidade de memória RAM para a VM (2GB, mais que o Debian para garantir uma instalação suave do Oracle Linux)disk_image
: Indica se estamos partindo de uma imagem de disco existente (false, pois estamos criando uma nova)output_directory
: Diretório onde a imagem será salvaaccelerator
: Utiliza KVM para aceleração de hardwaredisk_interface
enet_device
: Utilizam drivers virtio para melhor desempenhoformat
: Formato da imagem de saída (raw)boot_wait
: Tempo de espera antes de enviar comandos de bootboot_command
: Sequência de comandos enviados durante o boot para iniciar a instalação automatizada
O boot_command
é específico para o Oracle Linux. Primeiro, pressionamos a tecla “up” para selecionar a opção de instalação, depois “tab” para editar os parâmetros de boot, e finalmente adicionamos o parâmetro inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/ks.cfg
que indica ao instalador para usar nosso arquivo kickstart.
O http_directory
especifica o diretório onde o arquivo kickstart estará disponível. O Packer inicia um servidor HTTP temporário durante o build para disponibilizar este arquivo.
Os parâmetros relacionados a SSH são utilizados pelo Packer para se conectar à VM após a instalação do sistema operacional:
ssh_username
essh_password
: Credenciais para acesso SSH (definidas no arquivo kickstart)ssh_timeout
: Tempo máximo de espera pela disponibilidade do SSH (60 minutos, mais que o Debian devido ao tempo de instalação potencialmente maior do Oracle Linux)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
build {
sources = ["source.qemu.iso"]
# Envia o script de limpeza para a VM
provisioner "file" {
source = "cleanup.sh"
destination = "/tmp/cleanup.sh"
}
# Executa o script de limpeza na VM
provisioner "shell" {
inline = [
"chmod +x /tmp/cleanup.sh",
"echo 'packer' | sudo -S /tmp/cleanup.sh"
]
}
}
A seção final define o processo de build propriamente dito. Especificamos que vamos utilizar a fonte “qemu.iso” definida anteriormente e configuramos dois provisionadores:
- Um provisionador “file” que copia o script cleanup.sh para a VM
- Um provisionador “shell” que torna o script executável e o executa com privilégios de superusuário
O script cleanup.sh será responsável por realizar configurações adicionais e limpeza do sistema antes de finalizar a imagem.
Script de Limpeza e Preparação
Agora, vamos criar o script de limpeza e preparação que será executado dentro da VM durante o processo de build. Este script é fundamental para configurar o sistema adequadamente para uso como template.
1
vim cleanup.sh
Vamos analisar o conteúdo deste script em detalhes:
1
2
3
4
5
#!/usr/bin/env bash
# Atualização e instalação de pacotes
dnf update -y
dnf install -y cloud-init
O início do script atualiza todos os pacotes do sistema para as versões mais recentes e instala o cloud-init, que é essencial para a configuração inicial de instâncias em ambientes de nuvem. Diferente do Debian, o Oracle Linux utiliza o gerenciador de pacotes DNF, que é o sucessor do YUM.
1
2
3
4
5
6
# Configura o NoCloud como fonte de dados
cat <<EOF > /etc/cloud/cloud.cfg.d/99-nocloud-datasource.cfg
datasource_list:
- NoCloud
- None
EOF
Esta seção configura o cloud-init para usar o datasource NoCloud, que é adequado para ambientes de virtualização local como o KVM. O NoCloud permite que configurações sejam fornecidas através de um disco ISO ou um servidor web local, sem depender de um provedor de nuvem específico.
1
2
3
4
5
6
7
8
9
# Remoção de pacotes e dependências desnecessárias
dnf autoremove -y
dnf clean all
# Limpeza de cache, logs e arquivos temporários
rm -rf /var/cache/dnf
rm -rf /var/log/*
rm -rf /tmp/*
rm -rf /var/tmp/*
Aqui, removemos pacotes desnecessários e limpamos caches, logs e arquivos temporários para reduzir o tamanho da imagem final. O comando dnf autoremove
remove pacotes que foram instalados como dependências mas não são mais necessários, enquanto dnf clean all
limpa os caches do gerenciador de pacotes.
1
2
3
4
# Remoção de arquivos específicos da instância
truncate -s 0 /etc/machine-id
rm -f /etc/ssh/*key*
rm -f ~/.bash_history
Esta seção remove arquivos específicos da instância que não devem ser compartilhados entre múltiplas VMs:
- O machine-id é zerado para que cada nova instância receba um ID único
- As chaves SSH são removidas para que novas chaves sejam geradas em cada instância
- O histórico de comandos bash é removido para limpar rastros de configuração
1
2
# Zerar espaço livre
fstrim -av
O comando final, fstrim -av
, libera blocos de armazenamento não utilizados, o que pode reduzir significativamente o tamanho da imagem final quando ela for comprimida. A opção -a
aplica o comando a todos os sistemas de arquivos montados, e -v
ativa o modo verboso para mostrar o progresso.
Arquivo Kickstart
O arquivo kickstart é fundamental para automatizar a instalação do Oracle Linux, permitindo que todo o processo ocorra sem intervenção manual. Vamos criar o diretório http e o arquivo ks.cfg:
1
2
mkdir http
vim http/ks.cfg
Agora, vamos analisar o conteúdo deste arquivo em detalhes:
graphical
Esta primeira linha indica que a instalação deve usar o modo gráfico. Embora não vejamos a interface gráfica durante o build do Packer (que geralmente é headless), esta configuração garante que todos os pacotes necessários para um ambiente gráfico sejam instalados.
%addon com_redhat_kdump --enable --reserve-mb='auto'
%end
Esta seção configura o kdump, uma funcionalidade que permite capturar dumps de kernel em caso de falhas, útil para diagnóstico de problemas. A opção --reserve-mb='auto'
configura automaticamente a quantidade de memória reservada para o kdump.
keyboard --xlayouts='br'
lang pt_BR.UTF-8
Aqui, configuramos o layout de teclado como brasileiro (br) e o idioma como português do Brasil (pt_BR.UTF-8).
url --url="http://yum.oracle.com/repo/OracleLinux/OL9/baseos/latest/x86_64"
Esta linha especifica o repositório a ser usado durante a instalação. Estamos usando o repositório oficial da Oracle para o Oracle Linux 9, especificamente o repositório BaseOS para arquitetura x86_64.
%packages
@^minimal-environment
%end
Na seção de pacotes, selecionamos o grupo “minimal-environment”, que instala apenas os pacotes essenciais para um sistema funcional. Isso resulta em uma imagem menor e mais rápida de criar.
firstboot --enable
Esta opção habilita o assistente de primeiro boot, que será executado na primeira inicialização do sistema. No entanto, como estamos usando cloud-init, muitas das configurações que normalmente seriam feitas nesse assistente serão automatizadas.
ignoredisk --only-use=vda
autopart
clearpart --none --initlabel
Estas linhas configuram o particionamento:
ignoredisk --only-use=vda
: Usa apenas o disco vda (o primeiro disco virtual em ambientes KVM)autopart
: Utiliza o particionamento automáticoclearpart --none --initlabel
: Não remove partições existentes, apenas inicializa o rótulo do disco
timezone America/Sao_Paulo --utc
Configura o fuso horário para America/Sao_Paulo (horário de Brasília) e define que o relógio do hardware deve usar UTC.
rootpw --lock
user --groups=wheel --name=packer --password=$6$Jfl.H/kwDSeu7FWF$S/Mc/qsxM2DRupVkxKQawCgjQ3.i6beyCgY1fQCC0NUadW9pucgYnraGMWaCSg8g6t4GbozowE40X/FtAGert. --iscrypted --gecos="packer"
Estas linhas configuram os usuários:
rootpw --lock
: Bloqueia a conta root, uma prática recomendada de segurançauser --groups=wheel --name=packer...
: Cria um usuário chamado “packer” com acesso ao grupo wheel (que permite usar sudo)--password=...
: Define uma senha criptografada para o usuário--iscrypted
: Indica que a senha fornecida já está criptografada--gecos="packer"
: Define informações adicionais sobre o usuário
reboot
Esta opção faz com que o sistema reinicie automaticamente após a conclusão da instalação.
%post
echo "packer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/packer
/bin/chown root:root /etc/sudoers.d/packer
/bin/chmod 0440 /etc/sudoers.d/packer
%end
A seção %post
contém comandos que são executados após a instalação do sistema, mas antes do primeiro boot. Aqui, estamos:
- Criando um arquivo em /etc/sudoers.d/ que permite ao usuário “packer” executar comandos sudo sem senha
- Definindo o proprietário e o grupo do arquivo como root
- Configurando as permissões corretas (0440) para o arquivo
Esta configuração é necessária para que o Packer possa se conectar e provisionar a VM após a instalação.
Execução do Build
Com todos os arquivos configurados, estamos prontos para executar o build da nossa imagem. Primeiro, precisamos inicializar o Packer para baixar os plugins necessários:
1
packer init .
Este comando lê o arquivo os-install.pkr.hcl e baixa o plugin QEMU especificado na seção required_plugins.
Em seguida, podemos iniciar o build propriamente dito:
1
PACKER_LOG=1 packer build os-install.pkr.hcl
A variável de ambiente PACKER_LOG=1 ativa o modo de log detalhado, o que é útil para diagnosticar problemas caso ocorram. O comando packer build inicia o processo de build conforme definido no arquivo os-install.pkr.hcl.
Durante o build, o Packer realizará as seguintes etapas:
- Download da ISO do Oracle Linux (se ainda não estiver em cache)
- Criação de uma máquina virtual QEMU/KVM temporária
- Inicialização da VM com a ISO e execução do boot_command
- Instalação automatizada do Oracle Linux usando o arquivo kickstart
- Espera pela disponibilidade do SSH
- Execução dos provisionadores (cópia e execução do script cleanup.sh)
- Desligamento da VM
- Empacotamento da imagem resultante
Este processo pode levar alguns minutos, dependendo da velocidade do seu sistema e da sua conexão com a internet. Ao final, você terá uma imagem raw no diretório build/os-base/.
Conversão da Imagem
Após a conclusão do build, temos uma imagem no formato raw. Embora este formato seja eficiente para uso direto, o formato qcow2 (QEMU Copy-On-Write versão 2) oferece vantagens como compressão e suporte a snapshots. Vamos converter nossa imagem para qcow2:
1
qemu-img convert -O qcow2 -c build/os-base/ol9-kvm-template.raw /home/gean/kvm/templates/ol9-amd64.qcow2
Este comando utiliza a ferramenta qemu-img para converter a imagem:
-O qcow2
: Especifica o formato de saída como qcow2-c
: Ativa a compressão, reduzindo o tamanho do arquivo- O primeiro argumento é o caminho para a imagem de origem (formato raw)
- O segundo argumento é o caminho para a imagem de destino (formato qcow2)
Você pode ajustar o caminho de destino conforme sua preferência. Neste exemplo, estamos salvando a imagem em /home/gean/kvm/templates/, mas você pode escolher qualquer diretório que seja conveniente para seu ambiente.
Após a conversão, você terá uma imagem qcow2 comprimida pronta para uso em ambientes QEMU/KVM, como libvirt, OpenStack ou outras plataformas que suportem este formato.
Testando o Template com Terraform
Agora que temos nossa imagem pronta, vamos testá-la usando o Terraform. O Terraform nos permitirá provisionar uma VM usando nossa imagem e verificar se ela funciona conforme esperado.
Primeiro, vamos criar a estrutura de diretórios para nosso projeto Terraform:
1
2
mkdir -p ~/terraform/kvm
cd ~/terraform/kvm
Agora, vamos criar o arquivo principal de configuração do Terraform:
1
vim main.tf
Vamos analisar o conteúdo deste arquivo em detalhes:
1
2
3
4
5
6
7
terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
}
}
}
Esta seção inicial define o provider libvirt, que é necessário para interagir com o QEMU/KVM através da API libvirt. O provider é mantido pela comunidade e está disponível no Terraform Registry.
1
2
3
provider "libvirt" {
uri = "qemu:///system"
}
Aqui, configuramos o provider libvirt para se conectar ao daemon libvirt local através do URI “qemu:///system”. Este URI é o padrão para conexões locais com privilégios de sistema.
1
2
3
4
5
6
7
8
9
10
11
12
resource "libvirt_network" "olpacker" {
name = "olpacker"
mode = "nat"
addresses = ["10.3.4.0/24"]
autostart = true
dhcp {
enabled = false
}
dns {
enabled = true
}
}
Esta seção cria uma rede virtual chamada “olpacker” para nossa VM. A rede utiliza o modo NAT, que permite que a VM acesse a internet através do host, e é configurada com o range de endereços 10.3.4.0/24. Desativamos o DHCP porque configuraremos o endereço IP estaticamente, e ativamos o DNS para resolução de nomes.
1
2
3
4
5
6
resource "libvirt_volume" "os_image" {
name = "olpacker.qcow2"
pool = "default"
source = "/home/gean/kvm/templates/ol9-amd64.qcow2"
format = "qcow2"
}
Aqui, definimos o volume de disco para nossa VM. Especificamos o nome do volume, o pool de armazenamento (default), o caminho para nossa imagem qcow2 e o formato. O Terraform copiará a imagem para o pool de armazenamento libvirt.
1
2
3
4
5
6
7
data "template_file" "user_data" {
template = file("${path.module}/cloud_init.yml")
}
data "template_file" "network_config" {
template = file("${path.module}/network_cfg.yml")
}
Estas seções carregam os arquivos de configuração do cloud-init e da rede. O cloud-init é usado para configuração inicial da VM, como criação de usuários e execução de scripts, enquanto a configuração de rede define como a VM se conectará à rede.
1
2
3
4
5
6
resource "libvirt_cloudinit_disk" "cloudinit_olpacker" {
name = "cloudinit_olpacker.iso"
user_data = data.template_file.user_data.rendered
network_config = data.template_file.network_config.rendered
pool = "default"
}
Aqui, criamos um disco ISO contendo as configurações do cloud-init. Este disco será montado na VM durante o boot e usado pelo cloud-init para configurar o sistema.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
resource "libvirt_domain" "olpacker" {
name = "olpacker"
memory = "2048"
vcpu = 2
cpu {
mode = "host-passthrough"
}
cloudinit = libvirt_cloudinit_disk.cloudinit_olpacker.id
network_interface {
network_name = libvirt_network.olpacker.name
}
console {
type = "pty"
target_port = "0"
target_type = "serial"
}
disk {
volume_id = libvirt_volume.os_image.id
}
graphics {
type = "spice"
listen_type = "none"
}
}
Esta seção final define a VM propriamente dita. Configuramos:
- Nome da VM como “olpacker”
- 2GB de memória RAM
- 2 vCPUs
- Modo de CPU como “host-passthrough” para melhor desempenho
- Disco cloud-init criado anteriormente
- Interface de rede conectada à rede “olpacker”
- Console serial para acesso via linha de comando
- Disco principal usando o volume criado anteriormente
- Interface gráfica usando o protocolo SPICE
Agora, precisamos criar os arquivos de configuração do cloud-init. Primeiro, o arquivo cloud_init.yml:
1
vim cloud_init.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#cloud-config
users:
- name: gean
gecos: "Gean Martins"
sudo: "ALL=(ALL) NOPASSWD:ALL"
shell: /bin/bash
lock_passwd: false
ssh-authorized-keys:
- ${file("~/.ssh/tfvms.pub")}
ssh_pwauth: true
chpasswd:
list: |
root:<USE mkpasswd --method=SHA-512 PARA CRIAR A SENHA>
expire: false
runcmd:
- hostnamectl set-hostname olpacker
Este arquivo configura o cloud-init para:
- Criar um usuário chamado “gean” com acesso sudo sem senha
- Adicionar a chave SSH pública do arquivo ~/.ssh/tfvms.pub para autenticação sem senha
- Permitir autenticação por senha via SSH
- Definir uma senha para o usuário root (você deve substituir o placeholder por uma senha criptografada gerada com mkpasswd)
- Definir o hostname como “olpacker”
Para gerar uma senha criptografada para o usuário root, você pode usar o comando mkpasswd:
1
2
sudo apt-get install -y whois
mkpasswd --method=SHA-512
O comando solicitará uma senha e retornará a versão criptografada, que você deve usar no lugar do placeholder no arquivo cloud_init.yml.
Em seguida, criamos o arquivo de configuração de rede:
1
vim network_cfg.yml
1
2
3
4
5
6
7
8
9
10
11
#cloud-config
network:
version: 1
config:
- type: physical
name: ens3
subnets:
- type: static
address: 10.3.4.12/24
gateway: 10.3.4.1
dns_nameservers: 10.3.4.1
Este arquivo configura a rede da VM para:
- Usar a interface ens3 (padrão para VMs QEMU/KVM)
- Configurar o endereço IP estático 10.3.4.12/24
- Usar 10.3.4.1 (gateway da rede NAT) como servidor DNS e gateway
Note que a sintaxe da configuração de rede é diferente da usada no Debian. O Oracle Linux 9, sendo baseado no RHEL 9, utiliza o formato de configuração de rede versão 1, enquanto o Debian utiliza a versão 2.
Com todos os arquivos criados, podemos inicializar o Terraform e aplicar a configuração:
1
2
3
4
5
terraform init
terraform fmt
terraform validate
terraform plan
terraform apply
Estes comandos:
- Inicializam o diretório de trabalho do Terraform e baixam o provider libvirt
- Formatam os arquivos de configuração para seguir o estilo padrão
- Validam a sintaxe da configuração
- Mostram um plano de execução, detalhando o que será criado
- Aplicam o plano, criando os recursos definidos
Após a execução bem-sucedida do terraform apply
, você terá uma VM em execução usando a imagem que criamos. Você pode se conectar a ela via SSH usando o endereço IP configurado:
1
ssh gean@10.3.4.12
Ou, se preferir, pode acessar o console da VM usando o virsh:
1
virsh console olpacker
Para destruir a VM quando não precisar mais dela, execute:
1
terraform destroy
Este comando removerá todos os recursos criados pelo Terraform, incluindo a VM, o disco e a rede.
Considerações Finais
Neste tutorial, exploramos o processo completo de criação de um template Oracle Linux 9 para QEMU/KVM usando o Packer. Começamos com a configuração do ambiente KVM/Libvirt, instalação do Terraform e Packer, criação dos arquivos de configuração, execução do build da imagem, conversão para o formato qcow2 e finalmente teste da imagem usando o Terraform.
O template que criamos é uma base sólida para ambientes de virtualização e nuvem, com suporte a cloud-init para configuração dinâmica e personalização. Algumas possíveis personalizações e melhorias incluem:
- Adicionar pacotes específicos para seu caso de uso
- Configurar regras de firewall com firewalld (o firewall padrão do Oracle Linux)
- Implementar hardening de segurança seguindo as diretrizes da Oracle
- Otimizar o sistema para diferentes cargas de trabalho
- Adicionar monitoramento e logging
Para ambientes de produção, recomendamos:
- Usar senhas fortes ou, preferencialmente, autenticação por chave SSH
- Remover usuários e senhas padrão antes de disponibilizar a imagem
- Manter o sistema atualizado com as últimas correções de segurança
- Documentar todas as personalizações feitas no template
- Implementar um processo de atualização regular do template
O Oracle Linux 9 oferece várias vantagens para ambientes empresariais, incluindo:
- Compatibilidade com aplicações certificadas para Red Hat Enterprise Linux
- Acesso ao repositório Oracle UEK (Unbreakable Enterprise Kernel) para melhor desempenho
- Suporte de longo prazo da Oracle
- Integrações otimizadas com outros produtos Oracle
Com as ferramentas e técnicas apresentadas neste tutorial, você está bem equipado para criar e gerenciar templates de máquinas virtuais Oracle Linux 9 de forma eficiente e automatizada, seguindo as melhores práticas de infraestrutura como código.
Referências
- Documentação oficial do Packer
- Documentação do plugin QEMU para Packer
- Documentação do Oracle Linux 9
- Guia de instalação do Oracle Linux 9
- Documentação do Kickstart
- Documentação do cloud-init
- Documentação do Terraform
- Documentação do provider libvirt para Terraform
- Documentação do KVM
- Documentação do LIBVIRT
- Documentação do QEMU