Introdução Ao Libvirt
- Home /
- Linux /
- KVM e QEMU /
- Introdução ao Libvirt
Libvirt
O Libvirt é a última chave principal para começar a entender o mundo das virtualizações com KVM e QEMU. Basicamente, o libvirt é uma stack de ferramentas que contém uma API, ferramentas e daemon que são usados para administrar as máquinas virtuais. Ele simplifica todo o processo complexo de administração que teríamos ao usar comandos QEMU e KVM diretamente na linha de comando.
O libvirt também funciona com outros Hypervisors como o Xen, VMware e outros. As máquinas virtuais são definidas em arquivos XML que é lido pelo libvirt e traduzido para o hypervisor. Tanto as máquinas virtuais como Redes, Pools de armazenamento e outras configurações são definidas em arquivos XML.
O libvirt oferece o gerenciamento via linha de comando através do comando virsh. Com ele podemos administrar as máquinas virtuais e até definir novas configurações usando os arquivos XML. Também existe o virt-manager, uma interface gráfica utiliza o libvirt para gerenciar as máquinas de forma mais simples e amigável.
O libvirt também disponibiliza o libvirtd, um daemon que fica rodando no host para aceitar as solicitações de gerenciamento e executar as tarefas em background.
Objetos transitórios e persistentes
Praticamente tudo é tratado como um objeto no libvirt, como: máquinas virtuais, redes e pools de armazenamento. Eles podem existir de duas formas: transitória ou persistente.
Um objeto transitório existe apenas enquanto está em execução. Ele é criado diretamente em memória, sem que sua configuração seja salva em disco. Quando esse objeto é parado ou o serviço do libvirt é reiniciado, ele deixa de existir completamente, já que não existe nenhuma definição persistente armazenada para que possa ser recriado.
Já um objeto persistente possui sua configuração salva em um arquivo XML, mantido pelo libvirt no sistema de arquivos. Esse tipo de objeto existe independentemente de estar em execução ou não, podendo ser iniciado, parado e reiniciado quantas vezes forem necessárias, inclusive após um reboot do host.
Na prática, isso significa que um objeto persistente sempre pode ser recriado, pois o libvirt conhece sua definição. Os objetos persistentes são armazenadas no diretório /etc/libvirt. Dentro dele, cada tipo de objeto possui seu próprio subdiretório.
No caso das máquinas virtuais baseadas em QEMU/KVM, suas configurações são salvas em /etc/libvirt/qemu. Cada arquivo XML nesse diretório representa uma máquina virtual persistente. Mesmo que a VM esteja desligada, sua definição continua existindo ali, permitindo que o libvirt a inicialize novamente a qualquer momento.
O diretório /etc/libvirt/qemu/networks/ guarda as redes virtuais persistentes do libvirt. Já o diretório /etc/libvirt/storage/ armazenam as configurações persistentes dos Pools de armazenamento, seja ela local ou em rede.
Já o diretório /etc/libvirt/nwfilter/ faz parte do mecanismo de filtragem de rede, ele é usado para definir regras de firewall aplicadas diretamente às interfaces de rede das máquinas virtuais. Essas regras são chamadas de network filters (nwfilters) e servem para controlar, limitar ou bloquear determinados tipos de tráfego que entram ou saem de uma VM.
Storage Pools
O sistema de virtualização utiliza áreas reservadas para armanzenar os discos das máquinas virtuais. Essas áreas são conhecidas como Pools de Armazenamento. Elas podem ser discos inteiros, partições ou até diretórios dentro de um disco (ocupando espaço do dispositivo de armazenamento junto com outras aplicações). Esse Pool pode ser local ou remoto.
O libvirt usa o diretório /var/lib/libvirt/images/ como o diretório de armazenamento padrão.
Pool de armazenamento Local
Armazenamento local é conectado diretamente ao hospedeiro, os pools de armazenamento local incluem: diretórios locais, discos conectados diretamente, partições físicas e grupos de volumes LVM. Esses volumes de armazenamento armazenam imagens de máquinas virtuais convidadas ou são anexados a máquinas virtuais convidadas como armazenamento adicional.
Como os pools de armazenamento local são conectados diretamente ao hospedeiro, eles são úteis para desenvolvimento, teste e pequenas implantações que não exigem migração ou que necessitem ter um grande número de máquinas virtuais. Os pools de armazenamento local não são adequados para muitos ambientes de produção, pois os pools de armazenamento local não suportam a migração ao vivo.
Pool de armazenamento em Rede
Já os pools de armazenamento na Rede incluem dispositivos de armazenamento compartilhados na Rede usando um protocolos padrão. Esse tipo de armazenamento é muito usado em ambientes virtualizados que exigem mobilidade de máquinas virtuais entre hosts, especialmente em cenários de live migration sem cópia de disco.
O armazenamento em rede não é obrigatório para migração de máquinas virtuais (já que o virsh permite migrações com cópia do disco), mas ele é claramente a melhor escolha em ambientes de produção e clusters de virtualização.
Criando Storage Pools baseado em diretório
Existem várias formas de criar os Pools de armazenamento, vai depender do tipo local ou em rede e do protocolo no caso de Pools em rede. Para ver como se configurar alguns do métodos não descritos aqui, acesse a documentação oficial da RedHat.
# Primeiro crie o diretório novo:
$ sudo mkdir /libvirt-images
# Crie o pool (caso o diretório não exista, ele será criado):
$ virsh pool-define-as directory_pool --type dir --target /libvirt-images
Pool directory_pool defined
# Inicie o pool:
$ virsh pool-start directory_pool
Pool directory_pool started
# Habilite para ele iniciar no Boot:
$ virsh pool-autostart directory_pool
Pool directory_pool marked as autostarted
# Verifique se aparece no sistema:
$ virsh pool-list
Name State Autostart
---------------------------------------
default active yes
directory_pool active yes
# Verifique as informações do Pool
$ virsh pool-info directory_pool
Name: directory_pool
UUID: 5e857b9e-0f47-40a2-acb6-f3d48d573142
State: running
Persistent: yes
Autostart: yes
Capacity: 466,30 GiB
Allocation: 391,12 GiB
Available: 75,18 GiB
Perceba que ele obtém o tamanho total do disco, vamos configurar um pool que tenha um tamanho limitado, mas antes, teremos que excluir esse Pool:
# Primeiro desative o Pool, já que ele está em uso:
$ virsh pool-destroy directory_pool
Pool directory_pool destroyed
# Remova a definição do pool de dentro do sistema:
$ virsh pool-undefine directory_pool
Pool directory_pool has been undefined
# Podemos excluir o diretório usando o comando:
$ virsh pool-delete directory_pool
Pool directory_pool deleted
Vale deixar claro que, no contexto do libvirt, a maioria das ações administrativas chamadas de destroy não significam apagar ou remover definitivamente um objeto. O comando destroy equivale a um stop forçado, ou seja, ele apenas interrompe a execução daquele objeto no momento. O objeto continua existindo como definição persistente e pode ser iniciado novamente posteriormente.
Já a ação undefine é responsável por remover a persistência do objeto, apagando sua definição armazenada em XML pelo libvirt. Após um undefine, o objeto deixa de existir para o libvirt e não pode mais ser iniciado, a menos que seja recriado.
Por fim, a ação delete é a que realmente remove dados do disco, apagando arquivos ou volumes associados ao objeto, como diretórios, imagens de disco etc. Esse comando causa impacto direto nos dados e deve ser usado com cuidado.
Criando Storage Pools baseado em Disco
Este método uso um disco como pool ao invés de um diretório, vamos preparar nosso disco para receber o pool:
# Primeiro vamos preparar o disco:
$ sudo parted /dev/sda
GNU Parted 3.3
Usando /dev/sda
Bem vindo ao GNU Parted! Digite 'help' para ver uma lista de comandos.
(parted) mklabel
Novo tipo de rótulo de disco? gpt
Atenção: O rótulo de disco existente em /dev/sda será destruído e todos os dados neste disco serão perdidos. Você deseja continuar?
Sim/Yes/Não/No? Sim
(parted) quit
Informação: Você pode precisar atualizar /etc/fstab.
# Crei o pool:
$ virsh pool-define-as libvirt-pool_disk --type disk --target /dev/sda --source-dev /dev/sda --source-format gpt
Pool libvirt-pool_disk defined
# Inicie o pool:
$ virsh pool-start libvirt-pool_disk
Pool libvirt-pool_disk started
# Habilite para ele iniciar no Boot:
$ virsh pool-autostart libvirt-pool_disk
Pool libvirt-pool_disk marked as autostarted
# Verifique se aparece no sistema:
$ virsh pool-list
Name State Autostart
---------------------------------------
default active yes
libvirt-pool_disk active yes
# Verifique as informações do Pool:
$ virsh pool-info libvirt-pool_disk
Name: libvirt-pool_disk
UUID: 6e61a3cf-f7af-4966-8609-bd37cf6cc1c7
State: running
Persistent: yes
Autostart: yes
Capacity: 483,80 MiB
Allocation: 0,00 B
Available: 483,79 MiB
Nesse meu caso usei um pendrive. Perceba que não é necessário criar uma partição, apenas o disco, sem partição.
Para deletar é a mesma receita de bolo, mas nesse caso sem a parte do delete:
# Desativar o pool:
$ virsh pool-destroy libvirt-pool_disk
# Remover as configurações dele do sistema:
$ virsh pool-undefine libvirt-pool_disk
Criando Storage Pools baseado em Partição
O processo é bem parecido com o uso de disco, o que mudar é que aqui vamos informar as partições que vamos usar.
# Primeiro vamos preparar o disco:
$ sudo fdisk /dev/sda
Bem-vindo ao fdisk (util-linux 2.34).
As alterações permanecerão apenas na memória, até que você decida gravá-las.
Tenha cuidado antes de usar o comando de gravação.
Comando (m para ajuda): g
Criado um novo rótulo de disco GPT (GUID: 56A34CFC-A6F0-C74E-BC6E-2EAE0850A97B).
Comando (m para ajuda): n
Número da partição (1-128, padrão 1):
Primeiro setor (2048-990831, padrão 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-990831, padrão 990831):
Criada uma nova partição 1 do tipo "Linux filesystem" e de tamanho 482,8 MiB.
Partição nº 1: contém uma assinatura de vfat.
Deseja remover a assinatura? [S]im/[N]ão: s
A assinatura será removida por um comando de escrita.
Comando (m para ajuda): w
A tabela de partição foi alterada.
Chamando ioctl() para reler tabela de partição.
Sincronizando discos.
Estou usando o mesmo Pendrive de antes.
Agora com a partição criada, vamos criar o pool, a diferença aqui é o tipo de pool (fs para partição) e o --target vamos informar onde será montando a partição.
# Crie o pool:
$ virsh pool-define-as libvirt-pool_fs --type fs --target /mnt/libvirt-pool_fs --source-dev /dev/sda1 --source-format auto
# Faça o build para criar a pasta:
$ virsh pool-build libvirt-pool_fs
Pool libvirt-pool_fs built
# Inicie o pool:
$ virsh pool-start libvirt-pool_fs
# Habilite no boot:
$ virsh pool-autostart libvirt-pool_fs
# Liste os pools existentes:
$ virsh pool-list
Name State Autostart
---------------------------------------
default active yes
libvirt-pool_fs active yes
# Veja informações do pool:
$ virsh pool-info libvirt-pool_fs
Name: libvirt-pool_fs
UUID: 3467c0ec-5a18-478c-8184-fb7e0945cb3f
State: running
Persistent: yes
Autostart: yes
Capacity: 481,04 MiB
Allocation: 4,00 KiB
Available: 481,04 MiB
# Veja se a partição realmente foi montada:
$ df -h /dev/sda1
Sist. Arq. Tam. Usado Disp. Uso% Montado em
/dev/sda1 482M 4,0K 482M 1% /mnt/libvirt-pool_fs
# Desativar o pool:
$ virsh pool-destroy libvirt-pool_fs
# Remover as configurações dele do sistema:
$ virsh pool-undefine libvirt-pool_fs
Criando Storage Pools baseado em NFS
Não vou demonstrar como subir um servidor NFS, apenas como conectar em um já existente, mas antes de tudo, é necessário instalar os pacotes do NFS client:
| Pacotes | Distro | Descrição |
|---|---|---|
| nfs-common | Debian like | Utilitário para o Cliente NFS. |
| nfs-utils | RedHat like | Utilitário para o Cliente NFS e Servidor NFS. |
Crie um arquivo XML para armazenar a configuração do Pool que vamos criar:
<pool type='netfs'>
<name>NFSPool</name>
<source>
<host name='IP'/>
<dir path='Caminho'/>
<format type='auto'/>
</source>
<target>
<path>/var/lib/libvirt/images/NFSPool</path>
<permissions>
<mode>0755</mode>
<owner>0</owner>
<group>0</group>
<label>system_u:object_r:nfs_t:s0</label>
</permissions>
</target>
</pool>
Vejamos agora algumas descrições das opções acima:
| Opção | Descrição |
|---|---|
| host name=‘IP’ | Deve ser fornecido o IP do servidor NFS. |
| dir path | O caminho completo que foi configurado no arquivo /etc/exports do servidor NFS. |
<path>...</path> | O caminho de onde será montado o compartilhamento NFS. |
| permissions | Permissões usadas para montar o filesystem. |
| label | O rótulo SELinux para este diretório, isso é uma diretriz para solução de problemas na plataforma KVM. |
Volumes
Um volume é basicamente o HD da máquina virtual gerenciado pelo Libvirt. Enquanto que na máquina virtual isso representa o dispositivo de armazenamento em que o sistema convidado vai armazenar os dados, no libvirt, esses HDs são conhecidos como volumes.
É importante deixar bem claro que volumes não são exclusividade do Libvirt. O próprio QEMU é capaz de gerenciar discos virtuais, um exemplo simples é o uso do comando qemu-img create demonstrado no tópico sobre o QEMU. Portanto, estou explicando sobre volumes dentro do Libvirt porque ele será a ferramenta utilizada para gerenciar as VMs no geral.
Para referenciar um volume específico, três abordagens são possíveis:
O nome do volume e o conjunto de armazenamento
Um volume pode ser referenciado pelo nome, juntamente com o identificador para o conjunto de armazenamento ao qual ele pertence. Podemos ver isso usando o comando
virsh:# Liste as VMs: $ virsh list --all Id Name State -------------------------------------------- - centos7 shut off - Eve-NG shut off - rocky shut off - ubuntu20.04 shut off # Agora liste os volumes que estão no pool default: $ virsh vol-list default Name Path ------------------------------------------------------------------------------------------------------------------------------------------------------------------ centos-VAGRANTSLASH-7_vagrant_box_image_2004.01.img /var/lib/libvirt/images/centos-VAGRANTSLASH-7_vagrant_box_image_2004.01.img centos7.qcow2 /var/lib/libvirt/images/centos7.qcow2 focal-live-server-amd64.iso /var/lib/libvirt/images/focal-live-server-amd64.iso generic-VAGRANTSLASH-ubuntu2004_vagrant_box_image_3.2.14.img /var/lib/libvirt/images/generic-VAGRANTSLASH-ubuntu2004_vagrant_box_image_3.2.14.img generic-VAGRANTSLASH-ubuntu2004_vagrant_box_image_4.0.2.img /var/lib/libvirt/images/generic-VAGRANTSLASH-ubuntu2004_vagrant_box_image_4.0.2.img generic-VAGRANTSLASH-ubuntu2004_vagrant_box_image_4.0.2_box.img /var/lib/libvirt/images/generic-VAGRANTSLASH-ubuntu2004_vagrant_box_image_4.0.2_box.img rocky-clone.qcow2 /var/lib/libvirt/images/rocky-clone.qcow2 rocky.qcow2 /var/lib/libvirt/images/rocky.qcow2 te_default.img /var/lib/libvirt/images/te_default.img ubuntu20.04.qcow2 /var/lib/libvirt/images/ubuntu20.04.qcow2 Ubuntu_2004_first.qcow2 /var/lib/libvirt/images/Ubuntu_2004_first.qcow2 # Agora vamos ao que interessa, vamos verificar o volume de uma VM no pool default: $ virsh vol-info --pool default ubuntu20.04.qcow2 Name: ubuntu20.04.qcow2 Type: file Capacity: 15,00 GiB Allocation: 6,92 GiB
O caminho completo do volume
Um volume também pode ser referenciado pelo caminho completo do arquivo no sistema, se usarmos esse método não é necessário usar o identificador de pool.
Por exemplo, o volume denominado ubuntu20.04.qcow2, é visível para o sistema do hospedeiro tendo o caminho
/var/lib/libvirt/images/ubuntu20.04.qcow2, então nesse caso podemos chamar a imagem apenas referenciando o caminho completo.$ virsh vol-info /var/lib/libvirt/images/ubuntu20.04.qcow2 Name: ubuntu20.04.qcow2 Type: file Capacity: 15,00 GiB Allocation: 6,92 GiBUsando um identificador exclusivo
Quando um volume é criado no libvirt, ele recebe um identificador exclusivo chamado de chave de volume (volume key). Essa chave é usada internamente pelo libvirt para identificar o volume de forma única, independentemente do nome lógico que ele possa ter.
O formato dessa chave depende do tipo de armazenamento utilizado. Em pools baseados em arquivos, como o pool
dirpadrão, a chave do volume costuma ser o próprio caminho absoluto do arquivo. Por isso, nesses casos, a chave pode parecer idêntica ao caminho completo do volume no sistema de arquivos.Em outros tipos de armazenamento, como iSCSI, LVM ou storage remoto, a chave pode assumir formatos diferentes e não necessariamente corresponder a um caminho de arquivo.
Para visualizar a chave de um volume, podemos usar os comandos abaixo:
# Informando o pool ao qual o volume pertence: $ virsh vol-key --pool default --vol rocky-clone.qcow2 /var/lib/libvirt/images/rocky-clone.qcow2 # Informando diretamente o caminho do volume: $ virsh vol-key /var/lib/libvirt/images/ubuntu20.04.qcow2 /var/lib/libvirt/images/ubuntu20.04.qcow2Nesse exemplo, como os volumes estão armazenados em um pool baseado em diretório, a chave retornada pelo libvirt é igual ao caminho completo do arquivo no host.
Criando volumes
Criar volumes com virsh vol-create-as é apenas uma forma alternativa de criar discos virtuais, assim como foi demonstrado com o qemu-img. A diferença é que, ao usar o libvirt, o volume passa a ser registrado e gerenciado como um objeto do sistema, facilitando sua administração, associação a máquinas virtuais e remoção posterior.
# Crie um volume chamado 'volume1' de 8GB no formato qcow2:
$ virsh vol-create-as default volume1.qcow2 8G --format qcow2 --prealloc-metadata
Vol volume1.qcow2 created
# Veja se ele foi criado no pool:
$ virsh vol-list default | sed -n '1,2p; /volume1/p'
Name Path
-------------------------------------------------------
volume1.qcow2 /var/lib/libvirt/images/volume1.qcow2
Clonando volumes
Para facilitar a vida do administrador é possível clonar um volume já existente.
# Clone o volume:
$ virsh vol-clone --pool default volume1.qcow2 clone1
# Verifique se deu certo:
$ virsh vol-list default | sed -n '1,2p; /clone1/p'
Name Path
-------------------------------------------------------
clone1 /var/lib/libvirt/images/clone1
Rezise de volumes
Vamos ver como redimensionar os volumes, isso é bastante útil quando queremos aumentar o tamanho de um disco, vale ressaltar que ao aumentar o tamanho do disco, é necessário fazer com que o sistema da máquina virtual identifique o novo tamanho do disco e depois é preciso expandir o file system.
# Veja o tamanho do disco antes de mudarmos:
$ virsh vol-info /var/lib/libvirt/images/vol2.qcow2
Name: vol2.qcow2
Type: file
Capacity: 20,00 GiB
Allocation: 3,32 MiB
# Agora redimensione o tamanho do disco, deixando com 25GB:
$ virsh vol-resize --pool default vol2.qcow2 25G
Size of volume 'vol2.qcow2' successfully changed to 25G
## Pode ser feito com outro comando:
$ sudo qemu-img resize /var/lib/libvirt/images/vol2.qcow2 +5G
## Também pode ser feito assim:
$ sudo virsh blockresize ubuntu20.04 /var/lib/libvirt/images/ubuntu20.04.qcow2 40G
Block device '/var/lib/libvirt/images/ubuntu20.04.qcow2' is resized
# Veja o tamanho do disco novamente:
$ virsh vol-info /var/lib/libvirt/images/vol2.qcow2
Name: vol2.qcow2
Type: file
Capacity: 25,00 GiB
Allocation: 3,32 MiB
Anexar volumes nas VMs
Os volumes podem ser anexados a uma máquina virtual para fornecer dispositivos de armazenamento adicionais, é como adicionar mais discos em um computador.
Para anexar um volume baseado em arquivo, é necessário que o disco seja compatível com a VM. No nosso caso, já temos o volume volume1.qcow2, criado anteriormente no formato qcow2. A seguir, criamos um arquivo XML descrevendo o novo dispositivo de disco que será anexado à máquina virtual:
# Precisamos criar um arquivo xml para armazenar o conteúdo abaixo:
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/var/lib/libvirt/images/volume1.qcow2'/>
<target dev='vdb' bus='virtio'/>
</disk>
Esse XML define o tipo de armazenamento (file), o formato do disco, onde o volume está no host, o nome do dispositivo dentro da VM (vdb) e o barramento utilizado (virtio).
Com o arquivo XML criado, podemos anexar o disco à máquina virtual utilizando o virsh.
# O comando abaixo adiciona a configuração do novo disco no momento da execução do comando:
$ virsh attach-device ubuntu20.04 newdisk.xml --current
Device attached successfully
# O comando abaixo adiciona a configuração do novo disco na VM para o próximo boot:
$ virsh attach-device --config ubuntu20.04 newdisk.xml
Device attached successfully
Também é possível anexar discos a uma máquina virtual sem o uso de arquivos XML, utilizando o comando virsh attach-disk. Quando usado com a opção --current ou --live, o disco é adicionado apenas ao estado atual da VM e não é salvo na configuração persistente.
Isso significa que, ao desligar a máquina virtual, o disco anexado dessa forma será removido automaticamente. Para tornar a alteração permanente, é necessário utilizar a opção --config.
# Anexar disco sem usar xml:
$ virsh attach-disk --current ubuntu20.04 --source /var/lib/libvirt/images/vol2.qcow2 --target vdb
Disk attached successfully
# Veja os discos da VM usando o comando abaixo:
$ virsh domblklist --domain ubuntu20.04
Target Source
-----------------------------------------------------
vda /var/lib/libvirt/images/ubuntu20.04.qcow2
vdb /var/lib/libvirt/images/ubuntu.qcow2
vdc /var/lib/libvirt/images/vol2.qcow2
sda -
Desanexar volumes das VMs
Vamos ver como remover um volume de uma VM sem apagar esse volume:
# Veja quais discos a VM possui:
$ virsh domblklist --domain ubuntu20.04
Target Source
-----------------------------------------------------
vda /var/lib/libvirt/images/ubuntu20.04.qcow2
vdb /var/lib/libvirt/images/ubuntu.qcow2
vdc /var/lib/libvirt/images/vol2.qcow2
sda -
# Remove o disco VDB do domínio 'ubuntu20.04' (nome da VM):
$ virsh detach-disk ubuntu20.04 vdb --current
Disk detached successfully
Deletar um volume
Para deletar um volume podemos usar o comando abaixo:
$ virsh vol-delete --pool default volume1
Volumes base e clonagem de discos
Em ambientes virtualizados é comum criar várias máquinas virtuais a partir de um mesmo disco base. No libvirt e no QEMU isso é feito utilizando imagens com backing file, um recurso suportado principalmente pelo formato qcow2.
Nesse modelo, um volume base contém um sistema operacional já instalado e configurado, enquanto os demais volumes são criados como clones leves, que armazenam apenas as diferenças em relação ao disco base. O volume base permanece somente leitura, e cada VM grava suas alterações em seu próprio disco.
Essa abordagem é bastante usada pelo Vagrant ao criar novas máquinas virtuais e até por virtualizadores/emuladores como EVE-NG. Isso reduz drasticamente o uso de espaço em disco e acelera a criação de novas VMs.
Primeiro, é necessário ter um volume que servirá como base. Ele já deve ter um sistema operacional instalado:
$ ls -lh /var/lib/libvirt/images/base-ubuntu.qcow2
Esse disco não deve ser alterado depois de começar a ser usado como base. Com o volume base pronto, podemos criar novos volumes apontando para ele como backing file:
$ qemu-img create \
-f qcow2 \
-b /var/lib/libvirt/images/base-ubuntu.qcow2 \
/var/lib/libvirt/images/ubuntu-vm1.qcow2
O volume base-ubuntu.qcow2 é o volume base e o volume ubuntu-vm1.qcow2 é o volume da nova VM, esse que deve aparecer na VM e não o base.
O mesmo procedimento pode ser feito usando o libvirt:
$ virsh vol-create-as \
default \
ubuntu-vm2.qcow2 \
20G \
--format qcow2 \
--backing-vol base-ubuntu.qcow2 \
--backing-vol-format qcow2
O volume base não pode ser removido, isso resulta na falha de todas as VMs que usam ele como base. Também não é recomendado alterar o volume base, já que pode quebrar todas as VMs derivadas.
Não é recomendado para ambiente de produção.
Se for necessário tornar uma VM independente, é possível “quebrar” o vínculo com o volume base usando o comando abaixo:
$ qemu-img rebase -b "" ubuntu-vm1.qcow2
Após isso, o volume passa a conter todos os dados internamente.
Acessando os dados de um Discos Virtual
Os discos virtuais usados pelas máquinas virtuais (como qcow2, raw, dmg, vdi etc) são arquivos que ficam no servidor hospedeiro, esses arquivos contêm uma estrutura de disco completa como tabela de partições, sistemas de arquivos, bootloader, tudo dentro de um único arquivo.
Existem duas maneiras de acessar os dados contidos nesses discos virtuais que são: Acesso manual e o Acesso automatizado. Em alguns momentos é importante ter controle total sobre cada etapa do processo. Em outros, o objetivo é apenas acessar os arquivos de forma rápida, sem se preocupar com os detalhes internos do disco.
O acesso manual é feito a partir da associação do arquivo de imagem a um dispositivo de bloco, normalmente utilizando mecanismos como loop devices ou network block devices. Nesse método, o administrador reproduz manualmente o que o sistema faria ao conectar um disco físico.
Primeiro, a imagem do disco é apresentada ao kernel como um dispositivo de bloco. Em seguida, o sistema passa a enxergar a tabela de partições contida nesse disco. Por fim, o sistema de arquivos desejado é montado e os dados se tornam acessíveis.
Esse tipo de acesso é muito comum em imagens no formato raw, já que esse formato representa uma cópia bit a bit de um disco real. Como o conteúdo do arquivo corresponde diretamente ao layout do disco, o kernel consegue interpretá-lo sem camadas adicionais.
Imagens no formato qcow2 também podem ser acessadas manualmente, mas exigem um passo intermediário para serem expostas como um dispositivo de bloco. Por isso, em alguns cenários, os administradores convertem as imagens antes da montagem.
Já no uso de ferramentas que automatizam essa tarefa, o sistema é capaz de detectar automaticamente o formato do disco e tudo que é necessário para conseguir montar o disco, abstraindo o trabalho do administrador. Esse tipo de acesso é fornecido por comandos que fazem parte do ecossistema do Libvirt (o comando é o libguestfs, disponibilizado pelo pacote libguestfs-tools).
Tudo isso acontece sem que o usuário precise saber se o disco usa MBR, GPT, LVM ou até mesmo qual sistema de arquivos está presente. O foco deixa de ser o disco e passa a ser apenas o acesso aos arquivos.
Acesso via libguestfs
O comando guestmount é a forma mais simples de acessar os dados de um disco virtual sem precisar lidar diretamente com dispositivos de bloco, partições ou sistemas de arquivos.
# Instale o pacote caso ainda não esteja disponível no sistema
sudo apt install libguestfs-tools
# Monta o disco virtual
sudo guestmount -a /var/lib/libvirt/images/ubuntu.qcow2 -i /mnt
# Desmonta o disco
sudo guestunmount /mnt
À primeira vista, parece apenas um comando de montagem comum. No entanto, o que acontece por trás é bem diferente de um mount tradicional. Primeiro, o guestmount abre o arquivo da imagem e identifica o formato do disco, seja qcow2 ou raw.
Em seguida, ele cria um ambiente isolado em espaço de usuário, utilizando componentes do QEMU, sem expor o disco diretamente ao kernel do host. Depois disso, a ferramenta inspeciona o conteúdo do disco e identifica a tabela de partições, detecta volumes lógicos caso existam, e descobre qual sistema de arquivos está presente. Esse processo é o que a opção -i ativa, a inspeção automática.
Por fim, o sistema de arquivos encontrado é montado no diretório indicado, e os dados passam a ficar disponíveis como se estivessem em um diretório comum do sistema. Todo esse fluxo acontece sem a criação de loop devices, sem losetup, sem nbd e sem interação direta com o kernel para gerenciamento de partições.
Acesso manual em RAW
Na montagem manual, cada uma dessas etapas precisa ser executada explicitamente pelo administrador. Primeiro, o arquivo de imagem é associado a um dispositivo de bloco, normalmente usando um loop device no caso de imagens raw ou um network block device no caso de imagens qcow2.
Depois disso, é necessário garantir que o sistema reconheça a tabela de partições contida nesse disco. Somente após essa etapa é possível escolher qual partição será montada. Por fim, o sistema de arquivos é montado manualmente em um ponto de montagem definido.
# Primeiro vamos associar o arquivo a um loop device:
sudo losetup -fP /var/lib/libvirt/images/disk.raw
Com isso, o kernel vai criar um dispositivo como /dev/loop0. A opção -P faz com que as partições sejam expostas automaticamente, como /dev/loop0p1, /dev/loop0p2 e assim por diante.
# Agora é só montar a partição:
sudo mount /dev/loop0p1 /mnt
Ao terminar o acesso, o processo deve ser revertido com cuidado. Para desmontar, podemos usar os comandos abaixo:
# Desmontar a partição:
sudo umount /mnt
# Faça a desassociação do arquivo ou dispositivo associado ao loop device:
sudo losetup -d /dev/loop0
Acesso manual usando NBD
Para ter acesso a discos no formato QCOW2 temos que realizar uma abordagem diferente. Para isso, vamos usar o Network Block Device (NBD), ele é um mecanismo do kernel Linux que permite apresentar uma origem de dados externa como um dispositivo de bloco local.
A principal característica do NBD é que o conteúdo do disco não precisa estar fisicamente conectado à máquina. As operações de leitura e escrita realizadas pelo kernel são encaminhadas para um processo em espaço de usuário ou para um serviço remoto, que responde fornecendo os dados solicitados.
Do ponto de vista do kernel, não existe diferença entre um disco real e um disco exposto via NBD. O kernel apenas executa operações de bloco. Toda a lógica sobre onde os dados estão armazenados e como eles são obtidos fica fora do kernel.
Antes de começar digitar os comandos para montar o file system, o suporte ao NBD precisa estar disponível no kernel.
# Verifique se o módulo foi carregado (se não aparecer nada, é porque não foi):
sudo lsmod | grep nbd
# Carregue o módulo de precisar:
sudo modprobe nbd max_part=8
Com o módulo carregado, uma imagem de disco pode ser associada a um dispositivo NBD. No caso de imagens qcow2, o QEMU é utilizado para interpretar o formato e fornecer os dados ao kernel. Não se esqueça de verificar e trocar nbd0 caso já tenha essa associação no seu sistema.
sudo qemu-nbd --connect=/dev/nbd0 /var/lib/libvirt/images/disk.qcow2
Após esse comando, o sistema passa a enxergar um novo dispositivo de bloco em /dev/nbd0. Se a imagem contiver uma tabela de partições, elas também serão expostas, como /dev/nbd0p1, /dev/nbd0p2 e assim por diante. Antes de montar qualquer sistema de arquivos, é importante identificar a estrutura do disco:
lsblk /dev/nbd0
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nbd0 43:0 0 10G 0 disk
├─nbd0p1 43:1 0 10G 0 part
└─nbd0p9 43:9 0 8M 0 part
Agora que identificamos a partição, podemos montar o sistema de arquivos:
sudo mount /dev/nbd0p1 /mnt
Ao finalizar o uso do disco, o processo deve ser encerrado de forma ordenada. Primeiro, o sistema de arquivos deve ser desmontado e depois a imagem deve ser desconectada do dispositivo NBD.
# Desmonte o sistema de arquivos:
sudo umount /mnt
# Desconecte o dispositivo NBD:
sudo qemu-nbd --disconnect /dev/nbd0
Esse passo libera o dispositivo e encerra a comunicação entre o kernel e o QEMU.
guestfish
O comando guestfish é uma ferramenta que permite acessar e modificar diretamente o conteúdo de discos virtuais, sem precisar iniciar a máquina virtual ou montar o disco no sistema do host.
Ele faz parte do conjunto de ferramentas do libguestfs e oferece uma interface interativa que permite explorar o sistema de arquivos da VM e realizar alterações dentro da imagem.
# Instale o pacote caso ainda não tenha instalado:
sudo apt install -y libguestfs-tools
# Abre uma imagem de disco em modo leitura e escrita
sudo guestfish --rw -a /var/lib/libvirt/images/ubuntu.qcow2
# Dentro do guestfish
><fs> run
><fs> list-filesystems
/dev/sda1: xfs
/dev/rl_rocky8/root: xfs
/dev/rl_rocky8/swap: swap
><fs> mount /dev/rl_rocky8/root /
><fs> ls /etc
.pwd.lock
.updated
# Encerra a sessão salvando as alterações
exit
À primeira vista, o guestfish pode parecer apenas um shell interativo para manipular arquivos dentro de uma imagem de disco. Porém, internamente o funcionamento é bem mais interessante.
Assim como outras ferramentas do libguestfs, o guestfish abre o arquivo da imagem de disco e identifica automaticamente o formato utilizado, como qcow2, raw ou vmdk. Depois disso, ele cria um ambiente isolado em espaço de usuário que utiliza componentes do QEMU para acessar o disco virtual. Esse ambiente funciona como uma pequena máquina virtual auxiliar cuja única função é interpretar o sistema de arquivos contido na imagem.
Uma vez iniciado esse ambiente, o guestfish analisa a tabela de partições do disco, identifica volumes lógicos caso existam e detecta os sistemas de arquivos presentes. A partir desse ponto, você pode montar manualmente as partições dentro da sessão interativa e navegar pelo conteúdo do sistema como se estivesse trabalhando diretamente dentro da VM.
O guestfish é bem diferente do guestmount, já que ele fornece um conjunto próprio de comandos para manipular arquivos e diretórios dentro da imagem. Isso permite executar operações como:
- copiar arquivos para dentro ou para fora da imagem
- editar arquivos de configuração
- alterar permissões
- remover arquivos
- inspecionar diretórios
- modificar o conteúdo de arquivos sensíveis como
/etc/shadow
Esse tipo de acesso é extremamente útil em cenários onde iniciar a VM não é desejado. Por exemplo, quando uma imagem é utilizada como template para novas máquinas virtuais, iniciar o sistema poderia gerar alterações indesejadas como novos identificadores de sistema, logs ou chaves SSH.
virt-customize
O comando virt-customize permite modificar uma imagem de máquina virtual antes que ela seja executada. Ele faz parte do conjunto de ferramentas do libguestfs e foi criado justamente para automatizar tarefas de preparação de imagens, como instalar pacotes, alterar senhas ou copiar arquivos para dentro do sistema.
Diferente das ferramentas guestfish ou guestmount, que oferecem acesso interativo ao sistema de arquivos da imagem, o virt-customize executa uma série de operações de forma automática e declarativa, normalmente em um único comando.
# Instale o pacote caso ainda não tenha instalado:
sudo apt install -y libguestfs-tools
# Altera a senha do usuário root:
sudo virt-customize -a rocky8_default.img \
--root-password password:123456
# Instala um pacote dentro da imagem:
sudo virt-customize -a rocky8_default.img \
--install vim
[ 0.0] Examining the guest ...
[ 1.8] Setting a random seed
[ 1.8] Installing packages: vim
[ 26.1] SELinux relabelling
[ 26.3] Finishing off
# Copia um arquivo do host para dentro da imagem:
sudo virt-customize -a rocky8_default.img \
--copy-in arquivo.conf:/etc/
À primeira vista, o virt-customize parece apenas uma ferramenta que executa algumas modificações em uma imagem de disco. Porém, internamente ele utiliza o mesmo mecanismo do libguestfs para abrir o disco virtual e acessar seu sistema de arquivos sem precisar iniciar a máquina virtual.
Quando o comando é executado, a ferramenta abre o arquivo da imagem e identifica automaticamente seu formato, como qcow2 ou raw. Em seguida, é criado um ambiente isolado em espaço de usuário utilizando componentes do QEMU. Esse ambiente permite interpretar o conteúdo do disco e acessar suas partições e sistemas de arquivos.
Depois disso, o virt-customize inspeciona o disco, identifica o sistema operacional presente e monta automaticamente o sistema de arquivos principal. Com o sistema montado, a ferramenta executa as ações solicitadas pelo usuário, como copiar arquivos, instalar pacotes ou modificar configurações.
Essas operações são realizadas diretamente dentro da imagem, sem que o sistema operacional contido nela seja inicializado. Isso evita alterações indesejadas que normalmente ocorreriam durante o boot, como geração de novos identificadores de máquina, criação de logs ou mudanças em chaves SSH.
Network
Vamos entender como funciona a parte do gerenciamento de Redes, tanto virtual quanto física e dar uma rápida olhada sobre virtual switching.
Esse tópico é importante e se torna um pilar básico no gerencimento de máquinas virtuais, não é algo que acabará gerenciando sempre, mas com certeza é um passo fundamental quando está criando o projeto de virtualização.
Switches Virtuais
Os switches virtuais são componentes fundamentais em ambientes de virtualização, pois são eles que permitem conectar as máquinas virtuais entre si e com a rede externa. Funcionam de forma semelhante a um switch físico, porém inteiramente em software, dentro do host que executa as VMs.
Em um ambiente virtualizado, não é viável conectar cada máquina virtual diretamente a uma interface de rede física. Isso exigiria uma placa de rede dedicada para cada VM, além de portas extras no switch físico, o que tornaria o ambiente caro, ineficiente e difícil de escalar. O switch virtual resolve esse problema ao criar uma camada intermediária entre a rede física e as interfaces virtuais das máquinas convidadas.
Cada máquina virtual se conecta ao switch virtual por meio de uma interface de rede virtual. O switch virtual, por sua vez, pode encaminhar o tráfego entre as VMs, para o host ou para a rede externa, utilizando uma ou mais interfaces físicas do servidor como uplink. Dessa forma, dezenas ou centenas de máquinas virtuais podem compartilhar a mesma infraestrutura de rede física de forma organizada e eficiente.
Virtual Network Interface Card - vNIC
A vNIC (Virtual Network Interface Card) é a interface de rede virtual atribuída a uma máquina virtual. Ela cumpre o mesmo papel de uma placa de rede física, porém, existe apenas em software. Essa interface conecta a máquina virtual a um switch virtual, que por sua vez pode estar ligado ou não à rede física do host.
Na maioria dos cenários, o switch virtual utiliza uma ou mais interfaces físicas do host como uplink, permitindo que as máquinas virtuais se comuniquem com a rede externa. Dessa forma, o tráfego gerado pelas VMs passa pelo switch virtual, segue pela interface física do host e alcança o switch físico da rede.
É importante ter atenção a esse modelo. Caso o switch virtual utilize apenas uma interface física como uplink, uma falha nessa interface fará com que todas as máquinas virtuais percam conectividade externa, mesmo que continuem funcionando internamente. Nesse cenário, as VMs ainda conseguem se comunicar entre si dentro do mesmo switch virtual, da mesma forma que dispositivos conectados a um único switch físico continuam se comunicando mesmo que o uplink desse switch falhe.
Para evitar esse ponto único de falha, a solução é a mesma usada em redes físicas: redundância. O switch virtual pode ser configurado com mais de uma interface física como uplink, garantindo maior disponibilidade e, dependendo da tecnologia utilizada, até aumento de largura de banda. Assim, se uma interface falhar, a conectividade externa das máquinas virtuais é mantida.
No Linux, existem vários tipos de interfaces e mecanismos que podem ser usados para construir essa infraestrutura de rede virtual, cada um com objetivos específicos:
| Tipo | Descrição |
|---|---|
| Bridge | Interface de camada 2 que funciona como um switch virtual, permitindo que múltiplas interfaces (físicas ou virtuais) compartilhem o mesmo domínio de broadcast. Pode ou não estar conectada à rede física, dependendo das interfaces adicionadas a ela. |
| Bond | Combina múltiplas interfaces físicas em uma única interface lógica, oferecendo redundância e, dependendo do modo, balanceamento de carga (LACP). |
| Team | Alternativa mais moderna ao bond, focada em redundância e balanceamento, com arquitetura diferente. |
| MACVLAN | Cria múltiplos endereços MAC sobre uma única interface física, operando na camada 2. |
| IPVLAN | Similar ao MACVLAN, porém compartilha o mesmo endereço MAC e faz o encaminhamento na camada 3. |
| MACVTAP / IPVTAP | Extensões que integram conceitos de TAP, bridge e MACVLAN/IPVLAN, simplificando o caminho de rede para VMs. |
| VXLAN | Interface de sobreposição que permite criar redes de camada 2 sobre infraestruturas de camada 3, muito usada em ambientes distribuídos. |
| GRETAP / GRE | Protocolos de encapsulamento usados para transportar tráfego de camada 2 ou 3 através de redes IP. |
| GENEVE | Protocolo moderno de encapsulamento projetado para unificar e evoluir conceitos de VXLAN e GRE. |
| VETH | Par de interfaces virtuais interligadas, muito usadas para comunicação entre namespaces e containers, mas também aplicáveis em virtualização. |
NAT Network
A NAT Network é o tipo de rede padrão ao criar uma rede com o libvirt. Nesse modelo, as máquinas virtuais ficam conectadas a uma rede privada, isolada do restante da rede física, e acessam redes externas por meio de NAT (Network Address Translation).
Cada máquina virtual recebe um endereço IP privado, normalmente fornecido por um servidor DHCP interno do libvirt. Quando a VM tenta se comunicar com a rede externa (como a internet ou outra rede), o tráfego passa pelo host, que realiza a tradução do endereço IP privado da VM para o endereço IP da interface física do host.
Do ponto de vista da rede externa, todas as conexões parecem vir do host, e não diretamente das máquinas virtuais. Esse comportamento é semelhante ao de um roteador doméstico, onde vários dispositivos internos compartilham um único endereço IP público.
Esse modelo é ideal para ambientes de testes, laboratórios e desenvolvimento, pois oferece conectividade externa com um bom nível de isolamento e sem a necessidade de configurações avançadas na rede física.
Para melhor visualização segue um diagrama do funcionamento disponibilizado pela RedHat.

Routed Network
Na rede do tipo Routed, o switch virtual também cria uma rede separada para as máquinas virtuais, mas sem realizar tradução de endereços (NAT). Em vez disso, o tráfego das VMs é roteado entre a rede virtual e a rede física conectada ao host.
Nesse modelo, cada máquina virtual recebe um endereço IP pertencente a uma sub-rede distinta, diferente da rede física do host. O switch virtual atua como um roteador, encaminhando os pacotes entre essas redes com base nas informações de camada 3 contidas nos pacotes IP. Não existe mascaramento de endereços, e o IP da VM é preservado durante a comunicação.
Apesar disso, as máquinas físicas da rede não têm conhecimento automático da existência das VMs. Para que dispositivos externos consigam acessar as máquinas virtuais, é necessário configurar rotas explícitas no roteador físico ou em outros equipamentos de rede, apontando para a sub-rede das VMs através do host. Sem essa configuração manual, apenas as VMs conseguem iniciar conexões para fora, mas não recebem conexões vindas da rede física.
Esse modo é útil quando se deseja evitar NAT, manter endereçamento limpo e separar logicamente as redes, mas ainda assim controlar explicitamente quem pode acessar as máquinas virtuais. Para melhor visualização, segue um diagrama de funcionamento disponibilizado pela Red Hat:

Isolated Network
Na Isolated Network, as máquinas virtuais se comunicam apenas entre si, através de um switch virtual completamente isolado. Não existe qualquer ligação com a rede física do host, nem roteamento, nem NAT, nem acesso externo. Todo o tráfego permanece restrito ao domínio do switch virtual.
Esse tipo de rede cria um ambiente totalmente fechado, onde somente as VMs conectadas a esse switch virtual conseguem trocar pacotes. Nenhuma comunicação sai desse ambiente e nenhuma máquina externa consegue alcançá-lo, independentemente da configuração da rede física.
Esse modelo é ideal quando o objetivo é isolar tráfego sensível ou criar ambientes internos que não devem, em hipótese alguma, ter acesso direto à rede externa. Como não existe qualquer caminho para fora, o isolamento é garantido pelo próprio desenho da rede.
Um exemplo prático de uso é separar um banco de dados do acesso direto à rede. A VM que hospeda o banco pode estar conectada apenas a uma rede isolada, enquanto outra VM, responsável por consumir esses dados e disponibilizar informações ao público, possui duas interfaces de rede: uma conectada à rede isolada (para acessar o banco) e outra conectada a uma rede roteada ou NAT (para acesso externo). Dessa forma, o banco de dados nunca fica exposto à rede física, aumentando significativamente a segurança.
Para melhor visualização segue um diagrama do funcionamento disponibilizado pela RedHat.

Open Network
A Open Network é um modo de rede menos comum e mais “cru”, pensado para cenários em que o libvirt não deve interferir em absolutamente nada no tráfego, nem mesmo criando regras de firewall automaticamente.
Ela é conceitualmente parecida com a Routed Network, pois o tráfego das máquinas virtuais é encaminhado para a rede física usando a pilha de roteamento IP do host. As VMs ficam em uma sub-rede própria, diferente da LAN física, e o host atua como roteador entre essa rede virtual e a rede externa.
A diferença fundamental é que, no modo open, o libvirt não cria nenhuma regra de firewall. Nem regras para permitir tráfego, nem regras para bloquear, nem NAT, nem filtros automáticos. O libvirt apenas cria a rede virtual e encaminha os pacotes. Todo o resto fica sob responsabilidade do administrador.
Por causa disso, quando forward='open' é usado, não é permitido definir um dispositivo físico com dev=. Em outros modos, como route, o libvirt associa o encaminhamento a uma interface específica e cria regras de firewall em torno dela. No modo open, isso não acontece justamente porque a proposta é não tocar no firewall.
Esse modelo parte do pressuposto de que o roteador da LAN física já possui rotas configuradas apontando para o host, para que o tráfego consiga retornar às VMs. As regras de firewall necessárias já foram (ou serão) criadas manualmente, fora do libvirt, usando ferramentas como iptables, nftables ou firewalld.
Mesmo nesse modo, ainda é possível aplicar restrições específicas por máquina virtual usando nwfilter nas interfaces das VMs. Ou seja, a rede como um todo não tem firewall automático, mas você ainda pode filtrar tráfego por VM, se quiser.
Na prática, a Open Network é indicada apenas para ambientes onde você quer controle total da política de roteamento e firewall, não quer que o libvirt interfira.
Bridge Network
O modo Bridge é utilizado para conectar as máquinas virtuais diretamente à mesma rede física em que o host está conectado. Nesse modelo, as VMs passam a fazer parte do mesmo domínio de camada 2 da rede física, recebendo endereços IP da mesma faixa e podendo se comunicar diretamente com outros dispositivos da LAN, como se fossem máquinas físicas conectadas ao mesmo switch.
Diferente dos modos NAT, Routed ou Isolated, aqui não existe tradução de endereços nem roteamento intermediário feito pelo host. O switch virtual atua apenas como uma ponte (bridge), encaminhando os quadros entre as interfaces virtuais das VMs e a interface física do host.
Para melhor visualização, segue um diagrama do funcionamento disponibilizado pela RedHat.

Para utilizar uma rede do tipo Bridge, é necessário que o host possua uma interface física conectada à rede sem endereço IP configurado diretamente nela. O endereço IP passa a ser configurado na interface virtual do tipo bridge, que então incorpora essa interface física como porta. Dessa forma, tanto o host quanto as máquinas virtuais utilizam a bridge como ponto de conexão com a rede externa.
Mais adiante veremos na prática como criar e configurar uma bridge no host para ser utilizada pelo libvirt.
Rede Bridge versus Interface Bridge
No Linux e no ecossistema de virtualização e redes, a palavra bridge aparece em contextos diferentes, o que pode gerar confusão, pois embora usem o mesmo nome, representam conceitos diferentes.
Quando dizemos que uma rede é do tipo bridge, estamos falando de um modelo de conectividade no qual a máquina virtual passa a fazer parte da mesma rede de camada 2 em que o host está fisicamente conectado. Nesse cenário, a VM se comporta como um computador ligado ao switch físico, recebendo endereço IP do DHCP real da rede e podendo ser acessada diretamente pela LAN.
Agora, quando dizemos que uma interface é do tipo bridge, significa que todas as máquinas virtuais conectadas a essa mesma bridge estão ligadas ao mesmo switch virtual. No Linux, uma interface bridge cria, por padrão, um switch virtual de camada 2, permitindo que várias VMs sejam conectadas a ela e se comuniquem entre si como se estivessem no mesmo segmento de rede físico.
Todas as máquinas virtuais conectadas à mesma interface bridge compartilham o mesmo domínio de broadcast, assim como ocorreria em um switch físico. No entanto, a comunicação entre duas interfaces bridge distintas não ocorre automaticamente, a menos que exista algum mecanismo explícito de interligação entre esses switches virtuais.
No libvirt, qualquer tipo de rede virtual (como NAT, routed, isolated ou open) é implementada sobre uma interface do tipo bridge, pois esse tipo de interface é o que permite ao libvirt criar um switch virtual.
No entanto, uma rede do tipo bridge, no sentido de conectar a VM diretamente à rede física, não deve ser criada sobre outra interface bridge virtual, mas sim sobre uma interface física do host que não possua endereçamento IP configurado.
Gerenciando as Redes
Agora que entendemos as redes que é possível criar, vamos ver algumas opções do comando virsh net-* que são usados para trabalhar com redes no KVM.
| Opções | Descrição |
|---|---|
| net-list | Exibe todas as redes existentes. |
| net-info (Nome da Rede) | Mostra informações sobre uma rede. |
| net-destroy (Nome da Rede) | Desativa a rede do sistema (ainda pode ser usada). |
| net-undefine (Nome da Rede) | Exclui a rede do sistema (precisa do net-destroy para ter efeito imediato). |
| net-edit (Nome da Rede) | Edita a configuração da rede. |
| net-autostart (Nome da Rede) | Configura a rede para ser iniciada no Boot, a opção --disable podeser usada para não iniciar no Boot. |
| net-create (arquivo.xml) | Cria uma rede a partir de um arquivo XML e ativa ela, a rede não vai iniciar no Boot e não será persistente. |
| net-define (arquivo.xml) | Cria uma rede a partir de um arquivo XML que é iniciada no Boot e é persistente, mas não ativa ela. |
| net-start (Nome da Rede) | Inicia uma rede inativa. |
| net-uuid (Nome da Rede) | Converte o nome da rede para o UUID. |
| net-update (Nome da Rede) | Atualiza parte da configuração de uma rede existente (verifique as sub-opções). |
| net-dhcp-leases | Exibe os endereços DHCP emprestados. |
| net-dumpxml (Nome da Rede) | Obtém toda a configuração de uma rede. |
| uuidgen | Usado para gerar um UUID para quando formos criar uma rede. |
Agora vamos ver alguns comandos na prática.
# Primeiro, vamos descobrir quais redes possuímos:
$ virsh net-list
Name State Autostart Persistent
-----------------------------------------------------
default active yes yes
network_ipv6_onl active yes yes
vagrant-libvirt active no yes
# Liste as informações da rede 'defaut':
$ virsh net-info default
Name: default
UUID: 02bc4986-4e30-4292-9c3d-9f1df159035c
Active: yes
Persistent: yes
Autostart: yes
Bridge: virbr0
# Vamos ver como é a configuração da rede 'defaut':
$ virsh net-dumpxml default
<network>
<name>default</name>
<uuid>02bc4986-4e30-4292-9c3d-9f1df159035c</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:ea:b8:82'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
# Resultado de quando vemos um IP emprestado:
$ virsh net-dhcp-leases default
Expiry Time MAC address Protocol IP address Hostname Client ID or DUID
-----------------------------------------------------------------------------------------------------------
2022-06-27 11:23:56 52:54:00:da:49:69 ipv4 192.168.122.87/24 server 01:52:54:00:da:49:69
Como podemos ver no net-dumpxml acima, os tipos de rede como NAT, Routed, Isolated, Bridge e Open são definidos a partir do elemento <forward> no arquivo XML da rede. É esse atributo mode que define como o tráfego das VMs será encaminhado (ou não) para fora do switch virtual.
O elemento <forward> controla se e como o tráfego sai da rede virtual em direção ao host ou à rede física. É por isso que redes com topologias muito diferentes acabam sendo definidas no mesmo arquivo XML, mudando apenas esse campo.
Criando uma Rede NAT
O processo de criação de uma rede é um tanto simples, a parte mais dificíl seria gerar um endereço MAC para usarmos na nossa rede, mas até isso foi facilitado pela Red Hat que disponibilizou um script para gerar endereços MAC.
Como o prefixo das minhas redes do KVM começam com 52:54:00, no script eu alterei 0x00, 0x16, 0x3e para 0x52, 0x54, 0x00, assim os endereços MAC gerados pelo script vão ter o mesmo prefixo já usado no meu ambiente, isso mantém o padrão e facilita saber de onde é tal endereço MAC.
Vou deixar abaixo o script já alterado e corrigido para Python3:
#!/usr/bin/python3
import random
def randomMAC():
mac = [ 0x52, 0x54, 0x00,
random.randint(0x00, 0x7f),
random.randint(0x00, 0xff),
random.randint(0x00, 0xff) ]
return ':'.join(map(lambda x: "%02x" % x, mac))
print(randomMAC())
Vamos criar uma rede com NAT.
# Faça um dump da Rede default:
$ virsh net-dumpxml default > TutoNewNat.xml
# Agora vamos gerar um novo UUID:
$ uuidgen
944df9a1-960c-42fd-b020-e81537d3afcf
# Gere um novo MAC:
./macgen.py
52:54:00:7c:0c:0d
# Edite o arquivo 'TutoNewNat.xml' e altere o nome da rede: <name>default</name>;
# Mude o UUID: <uuid>944df9a1-960c-42fd-b020-e81537d3afcf</uuid>;
# Mude o MAC: <mac address='52:54:00:7c:0c:0d'/>;
# Mude o nome da interface: <bridge name='virbr2' stp='on' delay='0'/>
# Mude os IPs!
# Meu arquivo ficou assim:
<network>
<name>TutoNewNat</name>
<uuid>944df9a1-960c-42fd-b020-e81537d3afcf</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr2' stp='on' delay='0'/>
<mac address='52:54:00:7c:0c:0d'/>
<ip address='192.168.123.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.123.2' end='192.168.123.254'/>
</dhcp>
</ip>
</network>
# Agora crie a Rede:
$ virsh net-define TutoNewNat.xml
Network TutoNewNat defined from TutoNewNat.xml
# Ative a Rede:
$ virsh net-start TutoNewNat
Network TutoNewNat started
# Habilite para subir no Boot:
$ virsh net-autostart TutoNewNat
Network TutoNewNat marked as autostarted
# Verifique as redes existentes:
$ virsh net-list
Name State Autostart Persistent
----------------------------------------------------
default active yes yes
TutoNewNat active yes yes
vagrant-libvirt active no yes
Criando uma Rede Routed
Segue exemplo de uma Rede Routed.
<network>
<name>routed</name>
<forward mode='route'>
</forward>
<bridge name='virbr1' stp='on' delay='0'/>
<domain name='routed'/>
<ip address='172.16.20.1' netmask='255.255.255.0'>
<dhcp>
<range start='172.16.20.2' end='172.16.20.254'/>
</dhcp>
</ip>
</network>
Tive que incluir configuração de Firwall adicional para fazer funcionar.
Criando uma Rede Isolated
Segue exemplo de uma rede Isolated:
<network>
<name>isolated</name>
<domain name="isolated"/>
<ip address="192.168.151.1" netmask="255.255.255.0">
<dhcp>
<range start="192.168.151.2" end="192.168.151.254"/>
</dhcp>
</ip>
</network>
Criando uma Rede Open
Exemplo de Rede Open:
<network>
<name>Open</name>
<uuid>7e2d2faa-702d-4ac2-9dc2-aca62b390d10</uuid>
<forward mode='open'/>
<bridge name='virbr3' stp='on' delay='0'/>
<mac address='52:54:00:34:79:2e'/>
<domain name='Open'/>
<ip address='192.168.100.1' netmask='255.255.255.0'>
</ip>
</network>
Criando uma Rede Bridge
Vamos ver como configurar uma Rede em Bridge.
# Primeiro edite a configuração da Rede no host Hospedeiro:
$ sudo cat /etc/netplan/01-network-manager-all.yaml
network:
version: 2
renderer: NetworkManager
ethernets:
enxa:
dhcp4: false
bridges:
br0:
dhcp4: yes
interfaces:
- enxa
parameters:
stp: false
forward-delay: 0
version: 2
## Não se esqueça de mudar o nome da interface física 'enxa'!
# Agora crie um arquivo parecido com o abaixo:
$ cat kvmbridge.xml
<network>
<name>br0</name>
<forward mode="bridge"/>
<bridge name="br0"/>
</network>
# Crie a interface no libvirt:
$ virsh net-define kvmbridge.xml
Network br0 defined from kvmbridge.xml
# Inicie a interface:
$ virsh net-start br0
Network br0 started