Tutorial: Automatizando Infraestrutura KVM/LIBVIRT com Terraform
Aprenda a provisionar e gerenciar uma infraestrutura de virtualização completa com KVM/LIBVIRT e Terraform. Este guia passo a passo cobre a criação de VMs, redes, volumes e configuração automatizada com Cloud-Init para um ambiente robusto e escalável.
Introdução
Este tutorial detalha como utilizar o Terraform para provisionar e gerenciar uma infraestrutura de virtualização completa baseada em KVM/LIBVIRT. Abordaremos a criação de máquinas virtuais, redes, volumes de armazenamento e a configuração automatizada via Cloud-Init, proporcionando um ambiente robusto e escalável para seus projetos.
O objetivo é transformar os scripts Terraform existentes em um guia passo a passo, explicando cada componente e sua função, desde a configuração inicial até a implantação e verificação.
Pré-requisitos
Para seguir este tutorial, você precisará ter os seguintes componentes instalados e configurados em seu sistema:
Libvirt/KVM: Certifique-se de que o ambiente de virtualização KVM/LIBVIRT esteja instalado e funcionando corretamente em seu host Linux. Isso inclui o
libvirtd
e as ferramentas de linha de comando comovirsh
. Para instruções detalhadas de instalação e configuração do servidor KVM/LIBVIRT, consulte: https://geanmartins.com.br/posts/kvm-oracle-linux-7/Terraform: Baixe e instale a versão mais recente do Terraform (versão 1.5 ou superior é recomendada).
- Provedor Terraform para Libvirt: O provedor
dmacvicar/libvirt
será utilizado para interagir com o Libvirt. Ele será baixado automaticamente pelo Terraform, mas é importante garantir que seu ambiente tenha as dependências necessárias para ele funcionar. Isso inclui o próprio Libvirt e suas bibliotecas de desenvolvimento:- Debian/Ubuntu:
sudo apt install libvirt-dev
- RHEL/CentOS/Oracle Linux:
sudo yum install libvirt-devel
ousudo dnf install libvirt-devel
- Debian/Ubuntu:
Estação de Trabalho: Para configurar sua estação de trabalho com todas as ferramentas necessárias (Terraform, ferramentas de desenvolvimento, etc.), consulte: https://geanmartins.com.br/posts/workspace-ubuntu-2404/
Cloud-Init: As imagens base das máquinas virtuais devem ser compatíveis com Cloud-Init para que a configuração automatizada funcione. Certifique-se de que suas imagens (por exemplo,
debian-12-amd64.qcow2
,ol9-amd64.qcow2
) possuam ocloud-init
instalado.- Templates de Imagens: Você precisará de imagens base dos sistemas operacionais. Você pode:
- Baixar templates prontos:
- Debian: https://cloud.debian.org/images/cloud
- Oracle Linux: https://yum.oracle.com/oracle-linux-templates.html
- Criar seus próprios templates usando Packer: Para instruções sobre como criar templates personalizados, consulte: https://geanmartins.com.br/posts/tpl-deb12-qemu-packer/
- Baixar templates prontos:
- Pools de Armazenamento Libvirt: Você precisará ter dois pools de armazenamento configurados no Libvirt:
default
: Para armazenar os volumes das VMs em execução.templates
: Para armazenar as imagens base dos sistemas operacionais (templates).
Você pode criá-los usando comandos como:
1 2 3 4 5 6 7
sudo virsh pool-define-as default dir --target /var/lib/libvirt/images sudo virsh pool-start default sudo virsh pool-autostart default sudo virsh pool-define-as templates dir --target /var/lib/libvirt/templates sudo virsh pool-start templates sudo virsh pool-autostart templates
Certifique-se de que os diretórios
/var/lib/libvirt/images
e/var/lib/libvirt/templates
existam e tenham as permissões corretas. - Chave SSH: Uma chave SSH pública será necessária para acesso às VMs. Você pode gerá-la conforme as instruções fornecidas neste tutorial.
Com esses pré-requisitos atendidos, você estará pronto para começar a construir sua infraestrutura virtualizada com Terraform.
Download de Imagens Cloud-Init
Para facilitar o download das imagens base compatíveis com Cloud-Init para o seu pool de templates
, você pode usar os seguintes comandos. Certifique-se de que o pool templates
esteja configurado e acessível.
Exemplo para Debian 12 (Bookworm):
1
2
3
4
5
# Navegue até o diretório do seu pool de templates (ex: /var/lib/libvirt/templates)
cd /var/lib/libvirt/templates
# Baixe a imagem mais recente do Debian 12 Cloud (amd64)
wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2 -O debian-12-amd64.qcow2
Exemplo para Oracle Linux 9:
1
2
3
4
5
# Navegue até o diretório do seu pool de templates (ex: /var/lib/libvirt/templates)
cd /var/lib/libvirt/templates
# Baixe a imagem mais recente do Oracle Linux 9 Cloud (qcow2)
wget https://yum.oracle.com/templates/OracleLinux/OL9/u5/x86_64/OL9U5_x86_64-kvm-b259.qcow2 -O ol9-amd64.qcow2
Nota: Verifique sempre os links oficiais para as versões mais recentes das imagens, pois os URLs podem mudar com o tempo. Após o download, as imagens estarão prontas para serem utilizadas pelo Terraform em seu pool de templates
.
Estrutura do Projeto Terraform
O projeto Terraform é organizado em uma estrutura modular para facilitar a gestão e a escalabilidade. Cada arquivo .tf
ou diretório tem uma responsabilidade específica, conforme detalhado abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── cloud-init/ # Templates de configuração Cloud-Init
│ ├── network_config.yml # Template para configuração de rede das VMs
│ └── user_data.yml # Template para configuração de usuários e sistema
├── providers.tf # Configuração do provedor Terraform
├── variables.tf # Declaração de todas as variáveis do projeto
├── network.tf # Definição das redes virtuais LIBVIRT
├── volumes.tf # Criação de volumes de armazenamento para VMs
├── cloudinit.tf # Geração de discos Cloud-Init para cada VM
├── domain.tf # Definição das máquinas virtuais (domínios)
├── terraform.tfvars # Valores das variáveis principais
├── servers.auto.tfvars # Definição específica dos servidores
└── networks.auto.tfvars # Definição específica das redes
Esta estrutura permite uma clara separação de responsabilidades, onde cada arquivo lida com um aspecto específico da infraestrutura, desde a definição de redes até a criação de máquinas virtuais e suas configurações iniciais.
Entendendo os Arquivos Terraform
providers.tf
Este arquivo é fundamental para qualquer projeto Terraform, pois ele define os provedores que o Terraform utilizará para interagir com a infraestrutura. No nosso caso, ele configura o provedor libvirt
.
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
31
32
33
# =============================================================================
# CONFIGURAÇÃO DO PROVEDOR TERRAFORM
# =============================================================================
# Este arquivo define os requisitos de versão do Terraform e configura o
# provedor libvirt para gerenciar recursos de virtualização KVM/QEMU.
terraform {
# Versão mínima do Terraform necessária para executar este projeto
# Nota: A versão 1.5 é estável, mas sempre verifique a documentação oficial
# da HashiCorp para a versão mais recente e estável. Considere usar
# uma restrição de versão mais flexível (ex: `~> 1.5`) para permitir
# atualizações de patch sem quebrar a compatibilidade, ou uma versão
# específica para garantir reprodutibilidade.
required_version = ">= 1.5"
# Definição dos provedores necessários e suas versões
required_providers {
libvirt = {
source = "dmacvicar/libvirt" # Provedor oficial para libvirt
version = ">= 0.8.3" # Versão mais recente (atualizada em 21/07/2025)
}
}
}
# Configuração do provedor libvirt
provider "libvirt" {
# URI de conexão com o daemon libvirt
# Opções disponíveis:
# - "qemu:///system": Conexão local como root (padrão)
# - "qemu+ssh://user@host/system": Conexão remota via SSH
#uri = "qemu+ssh://gean@192.168.0.254/system" # Exemplo para conexão remota
uri = "qemu:///system" # Conexão local padrão
}
Explicação:
terraform { ... }
: Este bloco especifica a versão mínima do Terraform necessária e declara os provedores que serão utilizados. Aqui, definimos que precisamos do provedorlibvirt
na versão0.8.3
ou superior.provider "libvirt" { ... }
: Este bloco configura o provedorlibvirt
. A propriedadeuri
define como o Terraform se conectará ao daemonlibvirtd
. No exemplo,qemu:///system
indica uma conexão local. Você pode alterá-lo para uma conexão remota via SSH, se necessário.
variables.tf
Este arquivo centraliza a declaração de todas as variáveis utilizadas no projeto. Definir variáveis é uma boa prática no Terraform, pois permite reutilizar o código e adaptar a infraestrutura a diferentes ambientes sem modificar o código principal.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# =============================================================================
# DECLARAÇÃO DE VARIÁVEIS DO PROJETO
# =============================================================================
# Este arquivo centraliza todas as declarações de variáveis utilizadas no
# projeto, definindo tipos, descrições e valores padrão quando aplicável.
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE SERVIDORES
# -----------------------------------------------------------------------------
# Variável principal que define todas as máquinas virtuais a serem criadas.
# Cada servidor é definido como um objeto com especificações de hardware,
# sistema operacional e configuração de rede.
variable "servers" {
description = <<-EOT
Mapa de objetos que definem as máquinas virtuais a serem criadas.
Cada chave representa o nome da VM e o valor contém suas especificações:
- username: Nome do usuário personalizado (opcional, usa default_vm_user se não especificado)
- gecos: Nome completo do usuário (opcional, usa default_vm_user se não especificado)
- groups: Lista de grupos do usuário (opcional, usa os grupos padrão do SO)
- vcpus: Número de CPUs virtuais
- memory: Quantidade de memória RAM em MB (como string)
- os: Tipo de sistema operacional (deve existir em os_profiles)
- networks: Lista de interfaces de rede da VM
EOT
type = map(object({
username = optional(string) # Nome de usuário personalizado
gecos = optional(string) # Nome completo do usuário
groups = optional(list(string)) # Grupos adicionais do usuário
vcpus = number # Número de CPUs virtuais
memory = string # Memória RAM em MB
os = string # Perfil do sistema operacional
networks = list(object({
name = string
ipv4 = string
ipv4_prefix = optional(number)
ipv6 = string
ipv6_prefix = optional(number)
if_name = string
}))
}))
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE USUÁRIO PADRÃO
# -----------------------------------------------------------------------------
# Define as configurações padrão para o usuário principal das VMs quando
# não especificado individualmente em cada servidor.
variable "default_vm_user" {
description = <<-EOT
Configurações do usuário padrão aplicadas a todas as VMs que não
especificarem um usuário personalizado. Este usuário terá privilégios
administrativos (sudo/wheel) e acesso via chave SSH.
EOT
type = object({
name = string
gecos = string
})
}
# -----------------------------------------------------------------------------
# PERFIS DE SISTEMAS OPERACIONAIS
# -----------------------------------------------------------------------------
# Define templates e configurações específicas para cada tipo de SO suportado.
# Permite padronizar configurações como grupos de usuários e imagens base.
variable "os_profiles" {
description = <<-EOT
Mapa que define perfis completos para cada tipo de sistema operacional.
Cada perfil inclui:
- template_name: Nome do arquivo de template/imagem base no pool de templates
- default_groups: Lista de grupos padrão para usuários neste SO
Exemplos de grupos por SO:
- Debian/Ubuntu: ["users", "sudo"]
- RHEL/CentOS/Oracle: ["users", "wheel"]
EOT
type = map(object({
template_name = string
default_groups = list(string)
}))
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE POOLS DE ARMAZENAMENTO
# -----------------------------------------------------------------------------
# Define os pools de armazenamento utilizados para volumes das VMs e templates.
variable "volume_pool" {
description = <<-EOT
Nome do pool de armazenamento onde serão criados os volumes das máquinas
virtuais. Este pool deve existir no libvirt e ter espaço suficiente para
todos os volumes que serão criados.
EOT
type = string
default = "default"
}
variable "base_volume_pool" {
description = <<-EOT
Nome do pool de armazenamento onde estão localizados os templates/imagens
base dos sistemas operacionais. Estes templates servem como base para
criar os volumes das VMs através de copy-on-write.
EOT
type = string
default = "templates"
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE REDES VIRTUAIS
# -----------------------------------------------------------------------------
# Define todas as redes virtuais que serão criadas no ambiente.
variable "networks" {
description = <<-EOT
Mapa de objetos que definem as redes virtuais a serem criadas no libvirt.
Cada rede pode ter diferentes modos de operação:
- "nat": Rede com NAT para acesso à internet
- "none": Rede isolada sem DHCP (configuração manual via Cloud-Init)
- "bridge": Rede em bridge com interface física (requer configuração adicional)
EOT
type = map(object({
net_name = string
net_mode = string
ipv4_cidr = string
ipv6_cidr = string
}))
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE ACESSO SSH
# -----------------------------------------------------------------------------
# Chave pública SSH que será injetada em todas as VMs para acesso sem senha.
variable "ssh_public_key" {
description = <<-EOT
Chave pública SSH que será injetada em todas as máquinas virtuais para
permitir acesso sem senha. Esta chave deve corresponder à chave privada
que será usada para conectar às VMs.
Exemplo de geração: ssh-keygen -t ed25519 -C "email@example.com"
EOT
type = string
# Nota: Descomente a linha abaixo para marcar como sensível nos logs
#sensitive = true
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE REDE DMZ
# -----------------------------------------------------------------------------
# Define configurações de gateway e DNS para VMs na rede DMZ (zona desmilitarizada).
# Estas configurações são aplicadas via Cloud-Init nas VMs que não usam DHCP.
variable "network_dmz" {
description = <<-EOT
Configurações de rede para a zona DMZ, incluindo gateways e servidores DNS.
Estas configurações são aplicadas automaticamente via Cloud-Init em VMs
que possuem interfaces estáticas (não DHCP) na rede DMZ.
Componentes:
- gateway_v4/v6: Endereços do gateway padrão para IPv4 e IPv6
- ns1_v4/v6, ns2_v4/v6: Endereços dos servidores DNS primário e secundário
EOT
type = object({
gateway_v4 = string
gateway_v6 = string
ns1_v4 = string
ns1_v6 = string
ns2_v4 = string
ns2_v6 = string
})
}
Explicação:
Este arquivo define diversas variáveis que controlam a criação e configuração das VMs e redes. As principais são:
servers
: Um mapa complexo que define cada máquina virtual a ser criada, incluindo CPU, memória, sistema operacional e interfaces de rede. Permite personalizar cada VM individualmente.default_vm_user
: Define um usuário padrão para as VMs, caso não seja especificado um usuário personalizado para cada uma.os_profiles
: Um mapa que associa nomes de sistemas operacionais a templates de imagem e grupos de usuários padrão, facilitando a padronização.volume_pool
ebase_volume_pool
: Nomes dos pools de armazenamento do Libvirt onde os volumes das VMs e os templates base serão armazenados, respectivamente.networks
: Um mapa que define as redes virtuais a serem criadas, incluindo nome, modo de operação (NAT, isolada, etc.) e blocos CIDR IPv4/IPv6.ssh_public_key
: A chave pública SSH que será injetada em todas as VMs para acesso sem senha.network_dmz
: Configurações específicas para a rede DMZ (zona desmilitarizada), como gateways e servidores DNS, que serão aplicadas via Cloud-Init para VMs com IPs estáticos nesta rede.
network.tf
Este arquivo é responsável por criar as redes virtuais no Libvirt, conforme definido na variável networks
em variables.tf
e seus valores em networks.auto.tfvars
.
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
31
32
33
34
35
36
37
38
39
40
41
# =============================================================================
# DEFINIÇÃO DE REDES VIRTUAIS LIBVIRT
# =============================================================================
# Este arquivo cria todas as redes virtuais definidas na variável 'networks'.
# Cada rede pode ter diferentes configurações de modo, DHCP e endereçamento
# dependendo de sua finalidade na infraestrutura.
# Criação das redes virtuais baseada no mapa de configurações
resource "libvirt_network" "network" {
# Itera sobre cada rede definida em var.networks
for_each = var.networks
# Configurações básicas da rede
name = each.value.net_name # Nome da rede no libvirt
mode = each.value.net_mode # Modo de operação (nat, none, bridge, etc.)
autostart = true # Inicia automaticamente com o libvirt
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DINÂMICA DE DHCP
# -----------------------------------------------------------------------------
# O DHCP é habilitado apenas para redes que não estão no modo "none".
# Redes no modo "none" são tipicamente redes isoladas onde a configuração
# de IP é feita manualmente via Cloud-Init ou outros métodos.
dynamic "dhcp" {
# Cria o bloco DHCP apenas se o modo não for "none"
for_each = each.value.net_mode != "none" ? [1] : []
content {
enabled = true # Habilita o servidor DHCP da rede
}
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE ENDEREÇAMENTO
# -----------------------------------------------------------------------------
# Define os blocos CIDR IPv4 e IPv6 para redes que não estão no modo "none".
# Para redes no modo "none", o endereçamento é gerenciado externamente.
addresses = each.value.net_mode != "none" ? [
each.value.ipv4_cidr, # Bloco CIDR IPv4 (ex: 192.168.100.0/24)
each.value.ipv6_cidr # Bloco CIDR IPv6 (ex: fd12:ee::/64)
] : null # Sem endereçamento automático para redes isoladas
}
Explicação:
resource "libvirt_network" "network" { ... }
: Este bloco define um recurso de rede Libvirt. Ofor_each = var.networks
permite que o Terraform crie múltiplas redes, uma para cada entrada no mapanetworks
definido em suas variáveis.name
,mode
,autostart
: Definem o nome da rede, seu modo de operação (ex:nat
para redes com NAT,none
para redes isoladas,bridge
para bridge com interface física) e se ela deve iniciar automaticamente com o Libvirt.dynamic "dhcp" { ... }
: Este bloco configura o servidor DHCP para a rede. Ele é criado dinamicamente (ou seja, só é incluído se a condição for verdadeira) apenas se onet_mode
da rede não fornone
. Redes no modonone
são isoladas e não possuem DHCP gerenciado pelo Libvirt, exigindo configuração manual de IP nas VMs via Cloud-Init.addresses
: Define os blocos CIDR IPv4 e IPv6 para a rede. Assim como o DHCP, o endereçamento é configurado apenas para redes que não estão no modonone
.
volumes.tf
Este arquivo é responsável pela criação dos volumes de disco para cada máquina virtual. Ele utiliza o conceito de copy-on-write (qcow2) a partir de um template base, o que otimiza o uso de espaço em disco e agiliza a criação de VMs.
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
31
32
33
# =============================================================================
# CRIAÇÃO DE VOLUMES DE ARMAZENAMENTO PARA VMs
# =============================================================================
# Este arquivo cria volumes de disco para cada máquina virtual definida,
# utilizando templates base como origem através do mecanismo copy-on-write
# do formato qcow2, otimizando o uso de espaço em disco.
# Criação de volumes individuais para cada VM
resource "libvirt_volume" "os_image" {
# Itera sobre cada servidor definido em var.servers
for_each = { for vm_name, config in var.servers : vm_name => config }
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DO VOLUME
# -----------------------------------------------------------------------------
name = "${each.key}.qcow2" # Nome do arquivo do volume
pool = var.volume_pool # Pool onde o volume será criado
base_volume_name = var.os_profiles[each.value.os].template_name # Template base do SO
base_volume_pool = var.base_volume_pool # Pool onde está o template
format = "qcow2" # Formato do volume (copy-on-write)
# -----------------------------------------------------------------------------
# FUNCIONAMENTO DO COPY-ON-WRITE
# -----------------------------------------------------------------------------
# O volume criado é um "snapshot" do template base, ocupando inicialmente
# pouco espaço em disco. Conforme a VM escreve dados, apenas as diferenças
# são armazenadas no novo volume, otimizando o uso de armazenamento.
#
# Vantagens:
# - Criação rápida de VMs (não copia todo o template)
# - Economia de espaço (compartilha dados comuns)
# - Facilita atualizações de templates base
}
Explicação:
resource "libvirt_volume" "os_image" { ... }
: Este bloco cria um volume Libvirt para cada VM definida na variávelservers
.name
: Define o nome do arquivo do volume, usando o nome da VM como base.pool
: Especifica o pool de armazenamento (volume_pool
) onde o novo volume será criado.base_volume_name
ebase_volume_pool
: Indicam o nome do template base (definido emos_profiles
) e o pool onde este template está localizado. O Terraform usará este template para criar o novo volume.format = "qcow2"
: Define o formato do volume como qcow2, que permite o mecanismo de copy-on-write. Isso significa que o volume da VM não é uma cópia completa do template, mas sim uma camada que armazena apenas as diferenças em relação ao template base. Isso economiza espaço e acelera a criação de VMs.
cloudinit.tf
Este arquivo é crucial para a automação da configuração das máquinas virtuais. Ele gera discos Cloud-Init que são anexados às VMs e contêm instruções para configurar usuários, rede e outros aspectos do sistema durante o primeiro boot.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# =============================================================================
# GERAÇÃO DE DISCOS CLOUD-INIT PARA CONFIGURAÇÃO AUTOMÁTICA
# =============================================================================
# Este arquivo cria discos Cloud-Init para cada VM, contendo configurações
# de usuário, rede e sistema que são aplicadas automaticamente durante
# o primeiro boot da máquina virtual.
# Criação de discos Cloud-Init individuais para cada VM
resource "libvirt_cloudinit_disk" "cloudinit" {
# Itera sobre cada servidor definido em var.servers
for_each = var.servers
# Configurações básicas do disco Cloud-Init
name = "cloudinit-${each.key}.iso" # Nome do arquivo ISO do Cloud-Init
pool = var.volume_pool # Pool onde o disco será armazenado
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE DADOS DE USUÁRIO (USER DATA)
# -----------------------------------------------------------------------------
# O user_data contém configurações de sistema, usuários, pacotes e comandos
# que serão executados durante a inicialização da VM.
user_data = templatefile("${path.module}/cloud-init/user_data.yml", {
hostname = each.key # Nome da VM
user_name = coalesce(each.value.username, var.default_vm_user.name) # Nome do usuário (personalizado ou padrão)
gecos = coalesce(each.value.gecos, var.default_vm_user.gecos) # Nome completo do usuário
groups = coalesce(each.value.groups, var.os_profiles[each.value.os].default_groups) # Grupos do usuário
ssh_key = var.ssh_public_key # Chave SSH pública
})
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE REDE (NETWORK CONFIG)
# -----------------------------------------------------------------------------
# O network_config define a configuração de rede da VM, incluindo interfaces,
# endereços IP, rotas e servidores DNS. A configuração varia dependendo se
# a VM usa DHCP ou configuração estática.
network_config = templatefile(
"${path.module}/cloud-init/network_config.yml",
{
hostname = each.key # Nome da VM para referência
interfaces = each.value.networks # Lista de interfaces de rede
dmz_config = var.network_dmz # Configurações da rede DMZ
has_dhcp_interface = length([for iface in each.value.networks : iface if iface.ipv4 == "dhcp"]) > 0 # Verifica se há interface DHCP
}
)
# -----------------------------------------------------------------------------
# FUNCIONAMENTO DO CLOUD-INIT
# -----------------------------------------------------------------------------
# O Cloud-Init é executado automaticamente no primeiro boot da VM e:
# 1. Configura usuários e chaves SSH
# 2. Define configurações de rede
# 3. Executa comandos de inicialização
# 4. Instala pacotes se especificado
# 5. Configura hostname e outros parâmetros do sistema
}
Explicação:
resource "libvirt_cloudinit_disk" "cloudinit" { ... }
: Este bloco cria um disco Cloud-Init para cada VM definida emvar.servers
.name
epool
: Definem o nome do arquivo ISO do Cloud-Init e o pool de armazenamento onde ele será salvo.user_data
: Este é o coração da configuração de usuário. Ele usa a funçãotemplatefile
para preencher o templatecloud-init/user_data.yml
com informações específicas de cada VM, como hostname, nome de usuário, grupos e chave SSH pública. A funçãocoalesce
é usada para fornecer valores padrão caso não sejam especificados para uma VM individual.network_config
: Similar aouser_data
, este bloco usatemplatefile
para preencher o templatecloud-init/network_config.yml
com as configurações de rede da VM, incluindo interfaces, endereços IP (DHCP ou estático) e configurações de DMZ. A lógicahas_dhcp_interface
é usada para adaptar a configuração de rede com base na presença de interfaces DHCP.
domain.tf
Este é o arquivo que, de fato, cria as máquinas virtuais (domínios) no Libvirt, unindo todas as configurações definidas nos arquivos anteriores.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# =============================================================================
# DEFINIÇÃO DE MÁQUINAS VIRTUAIS (DOMÍNIOS LIBVIRT)
# =============================================================================
# Este arquivo cria as máquinas virtuais propriamente ditas, definindo
# especificações de hardware, discos, interfaces de rede e dispositivos
# de console para cada VM especificada na configuração.
# Criação das máquinas virtuais
resource "libvirt_domain" "domain" {
# Itera sobre cada servidor definido em var.servers
for_each = var.servers
# -----------------------------------------------------------------------------
# CONFIGURAÇÕES BÁSICAS DA VM
# -----------------------------------------------------------------------------
name = each.key # Nome da VM no libvirt
memory = each.value.memory # Quantidade de RAM em MB
vcpu = each.value.vcpus # Número de CPUs virtuais
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE CPU
# -----------------------------------------------------------------------------
# Utiliza passthrough do host para melhor performance, permitindo que a VM
# acesse diretamente as instruções e recursos da CPU física.
cpu {
mode = "host-passthrough" # Expõe todas as funcionalidades da CPU host
}
# -----------------------------------------------------------------------------
# DEPENDÊNCIAS DE RECURSOS
# -----------------------------------------------------------------------------
# Garante que redes e volumes sejam criados antes da VM
depends_on = [
libvirt_network.network, # Redes virtuais devem existir
libvirt_volume.os_image # Volumes de disco devem estar prontos
]
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DO CLOUD-INIT
# -----------------------------------------------------------------------------
# Anexa o disco Cloud-Init que contém as configurações de inicialização
cloudinit = libvirt_cloudinit_disk.cloudinit[each.key].id
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE DISCO PRINCIPAL
# -----------------------------------------------------------------------------
# Define o disco principal da VM usando o volume criado anteriormente
disk {
volume_id = libvirt_volume.os_image[each.key].id # Volume do sistema operacional
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DINÂMICA DE INTERFACES DE REDE
# -----------------------------------------------------------------------------
# Cria uma interface de rede para cada rede especificada na configuração da VM
dynamic "network_interface" {
for_each = each.value.networks # Itera sobre as redes da VM
content {
network_name = network_interface.value.name # Nome da rede virtual
wait_for_lease = network_interface.value.ipv4 == "dhcp" ? true : false # Aguarda lease DHCP se aplicável
# Nota: Para interfaces com IP estático, o endereçamento é configurado
# via Cloud-Init, não através do libvirt
}
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE CONSOLE SERIAL
# -----------------------------------------------------------------------------
# Permite acesso à VM via console serial para administração e troubleshooting
console {
type = "pty" # Tipo de console (pseudo-terminal)
target_type = "serial" # Console serial
target_port = "0" # Porta serial 0 (ttyS0)
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE INTERFACE GRÁFICA
# -----------------------------------------------------------------------------
# Configura acesso gráfico via protocolo SPICE para administração visual
graphics {
type = "spice" # Protocolo SPICE para melhor performance
listen_type = "address" # Escuta em endereço específico
autoport = true # Seleciona porta automaticamente
}
# -----------------------------------------------------------------------------
# RECURSOS ADICIONAIS DISPONÍVEIS
# -----------------------------------------------------------------------------
# Outros recursos que podem ser configurados conforme necessário:
# - Discos adicionais (múltiplos blocos "disk")
# - Dispositivos USB passthrough
# - Configurações de NUMA
# - Dispositivos de áudio
# - Redirecionamento de dispositivos
}
Explicação:
resource "libvirt_domain" "domain" { ... }
: Este bloco cria uma máquina virtual (domínio) no Libvirt para cada servidor definido emvar.servers
.name
,memory
,vcpu
: Definem o nome da VM, a quantidade de RAM e o número de vCPUs, respectivamente.cpu { mode = "host-passthrough" }
: Configura a CPU da VM para usar o modo host-passthrough, o que permite que a VM acesse diretamente as instruções e recursos da CPU física do host, resultando em melhor performance.depends_on
: Garante que as redes e os volumes de disco sejam criados e estejam prontos antes que a VM seja provisionada.cloudinit
: Anexa o disco Cloud-Init gerado emcloudinit.tf
à VM, permitindo que as configurações de usuário e rede sejam aplicadas automaticamente no primeiro boot.disk { volume_id = ... }
: Associa o volume de disco principal (criado emvolumes.tf
) à VM.dynamic "network_interface" { ... }
: Cria dinamicamente as interfaces de rede para a VM, com base nas configurações de rede definidas para cada servidor. Owait_for_lease
garante que a VM aguarde um lease DHCP se a interface estiver configurada para DHCP.console
egraphics
: Configuram o acesso ao console serial (para administração via terminal) e à interface gráfica via protocolo SPICE (para acesso visual à VM).
terraform.tfvars
Este arquivo é usado para atribuir valores às variáveis declaradas em variables.tf
. Ele contém as configurações principais e globais do projeto.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# =============================================================================
# CONFIGURAÇÕES PRINCIPAIS DO PROJETO TERRAFORM
# =============================================================================
# Este arquivo contém os valores das variáveis principais que definem o
# comportamento global da infraestrutura. Estas configurações são aplicadas
# a todos os recursos criados no projeto.
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE ACESSO SSH
# -----------------------------------------------------------------------------
# Chave pública SSH que será injetada em todas as VMs para acesso administrativo.
#
# Métodos alternativos para definir a chave:
# 1. Via linha de comando: terraform plan -var="ssh_public_key=$(cat ~/.ssh/kvm.pub)"
# 2. Via apply direto: terraform apply -var="ssh_public_key=$(cat ~/.ssh/kvm.pub)"
# 3. Definindo diretamente aqui (método atual)
ssh_public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAzaHM66H9EFpX/6aYcvFH85eHjyAsMVRk8DbfQhxxmI gean@inspiron"
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE POOLS DE ARMAZENAMENTO
# -----------------------------------------------------------------------------
# Define onde os volumes das VMs e templates base são armazenados no libvirt.
volume_pool = "default" # Pool para volumes das VMs em execução
base_volume_pool = "templates" # Pool para templates/imagens base dos SOs
# -----------------------------------------------------------------------------
# PERFIS DE SISTEMAS OPERACIONAIS SUPORTADOS
# -----------------------------------------------------------------------------
# Define as configurações específicas para cada tipo de SO que pode ser usado
# nas VMs, incluindo o nome do template e grupos padrão de usuários.
os_profiles = {
# Perfil para Debian 12
"debian12" = {
template_name = "debian-12-amd64.qcow2", # Arquivo template no pool base
default_groups = ["users", "sudo"] # Grupos padrão (sudo para admin)
},
# Perfil para Oracle Linux 9
"oracle9" = {
template_name = "ol9-amd64.qcow2", # Arquivo template no pool base
default_groups = ["users", "wheel"] # Grupos padrão (wheel para admin)
}
# Adicione novos perfis conforme necessário:
# "ubuntu22" = {
# template_name = "ubuntu-22.04-amd64.qcow2",
# default_groups = ["users", "sudo"]
# }
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE USUÁRIO PADRÃO
# -----------------------------------------------------------------------------
# Define o usuário administrativo padrão que será criado em todas as VMs
# quando não especificado individualmente na configuração do servidor.
default_vm_user = {
name = "suporte" # Nome de login do usuário
gecos = "Suporte User" # Nome completo/descrição do usuário
}
# -----------------------------------------------------------------------------
# CONFIGURAÇÕES DE REDE DMZ
# -----------------------------------------------------------------------------
# Define os parâmetros de rede para a zona DMZ (zona desmilitarizada),
# incluindo gateways e servidores DNS que serão configurados automaticamente
# nas VMs através do Cloud-Init.
network_dmz = {
# Gateways padrão para roteamento
gateway_v4 = "10.32.16.1", # Gateway IPv4 da rede DMZ
gateway_v6 = "fd00:32:16::1", # Gateway IPv6 da rede DMZ
# Servidores DNS primário e secundário
ns1_v4 = "10.32.16.3", # DNS primário IPv4 (ns1)
ns1_v6 = "fd00:32:16::3", # DNS primário IPv6 (ns1)
ns2_v4 = "10.32.16.4", # DNS secundário IPv4 (ns2)
ns2_v6 = "fd00:32:16::4" # DNS secundário IPv6 (ns2)
}
# -----------------------------------------------------------------------------
# NOTAS IMPORTANTES
# -----------------------------------------------------------------------------
# 1. Templates: Certifique-se de que os arquivos de template especificados
# em os_profiles existem no pool base_volume_pool antes de executar.
#
# 2. Pools: Os pools 'default' e 'templates' devem existir no libvirt.
# Crie-os com: virsh pool-define-as <nome> dir - - - - <caminho>
#
# 3. SSH: A chave privada correspondente à chave pública deve estar disponível
# para conectar às VMs após a criação.
#
# 4. Rede: As configurações de rede DMZ devem ser consistentes com a
# topologia real da infraestrutura.
Explicação:
Este arquivo define os valores para as variáveis globais do projeto, como:
ssh_public_key
: A chave pública SSH que será usada para acessar as VMs. É crucial que esta chave seja a sua chave pública para que você possa se conectar às máquinas virtuais após o provisionamento.volume_pool
ebase_volume_pool
: Os nomes dos pools de armazenamento do Libvirt. Certifique-se de que esses pools existam e estejam configurados corretamente em seu ambiente Libvirt.os_profiles
: Define os perfis de sistemas operacionais suportados, mapeando um nome amigável (ex:debian12
) para o nome do arquivo de template (debian-12-amd64.qcow2
) e os grupos padrão de usuários para aquele SO.default_vm_user
: O nome de usuário e o nome completo do usuário padrão que será criado em todas as VMs.network_dmz
: Contém as configurações de rede para a zona DMZ, incluindo gateways e servidores DNS. Essas informações são usadas pelo Cloud-Init para configurar as interfaces de rede estáticas das VMs na DMZ.
servers.auto.tfvars
Este arquivo é um .tfvars
especial que o Terraform carrega automaticamente. Ele é usado para definir as máquinas virtuais específicas que serão criadas, atribuindo valores à variável servers
declarada em variables.tf
.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# =============================================================================
# DEFINIÇÃO DE SERVIDORES DA INFRAESTRUTURA
# =============================================================================
# Este arquivo define todas as máquinas virtuais que serão criadas, incluindo
# suas especificações de hardware, sistema operacional e configuração de rede.
# Cada servidor tem um papel específico na arquitetura da infraestrutura.
servers = {
# ---------------------------------------------------------------------------
# SERVIDOR GATEWAY - Roteador e Firewall Principal
# ---------------------------------------------------------------------------
# Servidor que atua como gateway principal da infraestrutura, conectando
# múltiplas redes e fornecendo roteamento entre elas. Possui interfaces
# em todas as redes para permitir comunicação inter-redes.
gateway = {
vcpus = 2, # CPUs suficientes para roteamento e firewall
memory = "2048", # 2GB RAM para operações de rede
os = "debian12", # Debian para estabilidade e facilidade de configuração
# Configuração de múltiplas interfaces de rede
networks = [
# Interface externa - Conexão com internet via DHCP
{
name = "external",
ipv4 = "dhcp", ipv6 = "dhcp", # Obtém IP automaticamente do provedor
if_name = "ens3" # Nome da interface dentro da VM
},
# Interface DMZ - Rede de serviços públicos
{
name = "dmz",
ipv4 = "10.32.16.1", ipv4_prefix = 24, # Gateway da rede DMZ
ipv6 = "fd00:32:16::1", ipv6_prefix = 64, # Gateway IPv6 da DMZ
if_name = "ens4"
},
# Interface CGR - Rede corporativa/gestão
{
name = "cgr",
ipv4 = "10.48.32.1", ipv4_prefix = 24, # Gateway da rede CGR
ipv6 = "fd00:48:32::1", ipv6_prefix = 64, # Gateway IPv6 da CGR
if_name = "ens5"
},
# Interface DHCP - Rede para clientes DHCP
{
name = "dhcp",
ipv4 = "10.128.112.1", ipv4_prefix = 24, # Gateway da rede DHCP
ipv6 = "fd00:128:112::1", ipv6_prefix = 64, # Gateway IPv6 da DHCP
if_name = "ens6"
}
]
},
# ---------------------------------------------------------------------------
# SERVIDOR DNS PRIMÁRIO (NS1)
# ---------------------------------------------------------------------------
# Servidor DNS primário responsável pela resolução de nomes na infraestrutura.
# Localizado na rede DMZ para ser acessível por todas as outras redes.
ns1 = {
vcpus = 2, # CPUs adequadas para serviço DNS
memory = "2048", # 2GB RAM para cache DNS e operações
os = "oracle9", # Oracle Linux para ambiente empresarial
# Interface única na rede DMZ
networks = [
{
name = "dmz",
ipv4 = "10.32.16.3", ipv4_prefix = 24, # IP fixo do DNS primário
ipv6 = "fd00:32:16::3", ipv6_prefix = 64, # IPv6 do DNS primário
if_name = "ens3"
}
]
},
# ---------------------------------------------------------------------------
# SERVIDOR DNS SECUNDÁRIO (NS2)
# ---------------------------------------------------------------------------
# Servidor DNS secundário para redundância e balanceamento de carga.
# Sincroniza com o DNS primário e fornece resolução backup.
ns2 = {
vcpus = 2, # Especificações idênticas ao NS1
memory = "2048", # 2GB RAM para operações DNS
os = "oracle9", # Mesmo SO do NS1 para consistência
# Interface única na rede DMZ
networks = [
{
name = "dmz",
ipv4 = "10.32.16.4", ipv4_prefix = 24, # IP fixo do DNS secundário
ipv6 = "fd00:32:16::4", ipv6_prefix = 64, # IPv6 do DNS secundário
if_name = "ens3"
}
]
}
# ---------------------------------------------------------------------------
# SERVIDORES ADICIONAIS
# ---------------------------------------------------------------------------
# Outros servidores podem ser definidos aqui seguindo o mesmo padrão.
# Exemplos de servidores comuns em infraestruturas:
#
# web1 = {
# vcpus = 4,
# memory = "4096",
# os = "debian12",
# networks = [
# { name = "dmz", ipv4 = "10.32.16.10", ipv4_prefix = 24,
# ipv6 = "fd00:32:16::10", ipv6_prefix = 64, if_name = "ens3" }
# ]
# },
#
# db1 = {
# vcpus = 8,
# memory = "8192",
# os = "oracle9",
# networks = [
# { name = "cgr", ipv4 = "10.48.32.10", ipv4_prefix = 24,
# ipv6 = "fd00:48:32::10", ipv6_prefix = 64, if_name = "ens3" }
# ]
# }
# Placeholder para indicar que outros servidores foram omitidos
# [...]
}
# =============================================================================
# NOTAS SOBRE CONFIGURAÇÃO DE SERVIDORES
# =============================================================================
#
# ESPECIFICAÇÕES DE HARDWARE:
# - vcpus: Ajuste conforme a carga esperada do serviço
# - memory: Especificado como string em MB (ex: "2048" = 2GB)
# - Considere reservar recursos para o host físico
#
# CONFIGURAÇÃO DE REDE:
# - Cada servidor pode ter múltiplas interfaces
# - IPs estáticos são configurados via Cloud-Init
# - DHCP pode ser usado em redes que suportam
# - Prefixos de rede devem ser consistentes com a topologia
#
# SISTEMAS OPERACIONAIS:
# - Use perfis definidos em terraform.tfvars (os_profiles)
# - Certifique-se de que os templates existem no pool base
# - Considere padronizar SOs por função (ex: Debian para web, Oracle para DB)
#
# NOMENCLATURA:
# - Use nomes descritivos que indiquem a função do servidor
# - Mantenha consistência na convenção de nomes
# - Considere incluir ambiente (dev, prod) se aplicável
Explicação:
Este arquivo define as especificações de cada máquina virtual a ser criada. Cada bloco dentro de servers
representa uma VM, com as seguintes propriedades:
- Nome da VM (ex:
gateway
,ns1
,ns2
): A chave do mapaservers
é o nome da máquina virtual. vcpus
: Número de CPUs virtuais alocadas para a VM.memory
: Quantidade de memória RAM em MB (especificada como string).os
: O perfil do sistema operacional a ser usado, que deve corresponder a um dos perfis definidos emos_profiles
noterraform.tfvars
.networks
: Uma lista de objetos, onde cada objeto representa uma interface de rede para a VM. Para cada interface, você define:name
: O nome da rede virtual Libvirt à qual a interface será conectada.ipv4
eipv6
: O endereço IP (oudhcp
para obter um IP via DHCP).ipv4_prefix
eipv6_prefix
: O prefixo da rede (ex: 24 para /24).if_name
: O nome da interface de rede dentro da VM (ex:ens3
).
Este arquivo permite uma flexibilidade enorme para definir a topologia da sua infraestrutura, criando diferentes tipos de servidores com configurações de hardware e rede personalizadas.
networks.auto.tfvars
Similar ao servers.auto.tfvars
, este arquivo é carregado automaticamente pelo Terraform e é usado para definir as redes virtuais específicas que serão criadas, atribuindo valores à variável networks
declarada em variables.tf
.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# =============================================================================
# DEFINIÇÃO DE REDES VIRTUAIS DA INFRAESTRUTURA
# =============================================================================
# Este arquivo define todas as redes virtuais que serão criadas no ambiente
# libvirt. Cada rede tem uma finalidade específica na arquitetura e pode
# operar em diferentes modos dependendo dos requisitos de conectividade.
networks = {
# ---------------------------------------------------------------------------
# REDE EXTERNA - Conexão com Internet
# ---------------------------------------------------------------------------
# Rede que fornece conectividade com a internet através de NAT.
# Utilizada pelo gateway para obter conectividade externa.
external = {
net_name = "external" # Nome da rede no libvirt
net_mode = "nat" # Modo NAT para acesso à internet
ipv4_cidr = "192.168.100.0/24" # Bloco IPv4 gerenciado pelo libvirt
ipv6_cidr = "fd12:ee::/64" # Bloco IPv6 gerenciado pelo libvirt
},
# ---------------------------------------------------------------------------
# REDE DMZ - Zona Desmilitarizada
# ---------------------------------------------------------------------------
# Rede isolada para serviços que precisam ser acessíveis externamente
# mas devem ser segregados da rede interna. Inclui servidores DNS,
# web servers e outros serviços públicos.
dmz = {
net_name = "dmz" # Nome da rede no libvirt
net_mode = "none" # Rede isolada sem DHCP automático
ipv4_cidr = "10.32.16.0/24" # Bloco IPv4 da DMZ (configuração manual)
ipv6_cidr = "fd00:32:16::/64" # Bloco IPv6 da DMZ (configuração manual)
},
# ---------------------------------------------------------------------------
# REDE CGR - Centro de Gerência de Redes
# ---------------------------------------------------------------------------
# Rede que terá acesso administrativo a toda a infraestrutura.
# Acesso SSH a rede DMZ, por exemplo, só será permitido a partir desta rede
# As demais serão bloqueadas no firewall.
cgr = {
net_name = "cgr" # Nome da rede no libvirt
net_mode = "none" # Rede isolada sem DHCP automático
ipv4_cidr = "10.48.32.0/24" # Bloco IPv4 da rede corporativa
ipv6_cidr = "fd00:48:32::/64" # Bloco IPv6 da rede corporativa
},
# ---------------------------------------------------------------------------
# REDE DHCP - Clientes Dinâmicos
# ---------------------------------------------------------------------------
# Rede para dispositivos que obtêm configuração automaticamente via DHCP.
# Utilizada para estações de trabalho, dispositivos temporários e
# sistemas que não requerem IPs fixos.
dhcp = {
net_name = "dhcp" # Nome da rede no libvirt
net_mode = "none" # Rede isolada (DHCP gerenciado externamente)
ipv4_cidr = "10.128.112.0/20" # Bloco IPv4 maior para muitos clientes
ipv6_cidr = "fd00:128:112::/64" # Bloco IPv6 para clientes DHCP
}
}
# =============================================================================
# NOTAS IMPORTANTES SOBRE CONFIGURAÇÃO DE REDES
# =============================================================================
#
# ENDEREÇAMENTO IP:
# - Os blocos CIDR definidos aqui são principalmente para documentação
# - Para redes com net_mode = "none", o endereçamento é configurado manualmente
# - Para redes com net_mode = "nat", o libvirt gerencia o endereçamento automaticamente
# - Certifique-se de que não há sobreposição entre blocos de diferentes redes
#
# MODOS DE REDE DISPONÍVEIS:
# - "nat": Rede com NAT para acesso à internet (DHCP automático)
# - "none": Rede isolada sem serviços automáticos (configuração manual)
# - "bridge": Rede em bridge com interface física (requer configuração adicional)
# - "route": Rede roteada sem NAT (requer configuração de roteamento)
#
# PLANEJAMENTO DE ENDEREÇOS:
# - Use blocos RFC 1918 para redes privadas (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
# - Use endereços IPv6 ULA (Unique Local Address) com prefixo fd00::/8
# - Mantenha consistência na numeração entre IPv4 e IPv6
# - Reserve espaço para crescimento futuro da rede
#
# SEGURANÇA E ISOLAMENTO:
# - Redes com mode "none" são completamente isoladas por padrão
# - O roteamento entre redes deve ser configurado no gateway
# - Implemente regras de firewall apropriadas no gateway
# - Considere VLANs adicionais para maior segmentação se necessário
#
# DHCP E DNS:
# - Para redes isoladas, configure DHCP no gateway se necessário
# - Configure encaminhamento DNS apropriado para resolução de nomes
# - Mantenha registros DNS atualizados para IPs estáticos
Explicação:
Este arquivo define as redes virtuais que serão criadas no ambiente Libvirt. Cada bloco dentro de networks
representa uma rede, com as seguintes propriedades:
- Nome da Rede (ex:
external
,dmz
,cgr
,dhcp
): A chave do mapanetworks
é o nome da rede virtual. net_name
: O nome da rede no Libvirt.net_mode
: O modo de operação da rede. Pode sernat
(com NAT para acesso à internet),none
(rede isolada sem DHCP automático, exigindo configuração manual de IP nas VMs), oubridge
(rede em bridge com interface física).ipv4_cidr
eipv6_cidr
: Os blocos CIDR IPv4 e IPv6 da rede. Para redes com `net_mode =
none, esses CIDRs são principalmente para documentação, pois o endereçamento é configurado manualmente via Cloud-Init. Para redes
nat`, o Libvirt gerencia o endereçamento automaticamente.
Este arquivo permite definir a topologia de rede da sua infraestrutura, criando redes com diferentes propósitos e modos de operação para isolamento e conectividade.
cloud-init/network_config.yml
Este é um template de configuração de rede para o Cloud-Init, que será usado para configurar as interfaces de rede dentro das máquinas virtuais. Ele suporta tanto configurações DHCP quanto estáticas, com lógica para roteamento e DNS.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#cloud-config
# =============================================================================
# TEMPLATE DE CONFIGURAÇÃO DE REDE CLOUD-INIT
# =============================================================================
# Este template gera a configuração de rede para cada VM baseado em suas
# interfaces definidas. Suporta tanto configuração DHCP quanto estática,
# com roteamento e DNS configurados automaticamente conforme necessário.
network:
version: 2 # Versão 2 do formato netplan/cloud-init
ethernets:
%{ for iface in interfaces ~}
# -------------------------------------------------------------------------
# INTERFACE: ${iface.if_name} (Rede: ${iface.name})
# -------------------------------------------------------------------------
${iface.if_name}:
%{ if iface.ipv4 == "dhcp" }
# Configuração DHCP - Obtém IP automaticamente
dhcp4: true # Habilita DHCP para IPv4
dhcp6: true # Habilita DHCP para IPv6
%{ else }
# Configuração estática - IPs definidos manualmente
dhcp4: no # Desabilita DHCP para IPv4
dhcp6: no # Desabilita DHCP para IPv6
accept-ra: false # Desabilita Router Advertisement IPv6
# Endereços IP estáticos da interface
addresses:
- ${iface.ipv4}/${iface.ipv4_prefix} # Endereço IPv4 com prefixo
%{ if iface.ipv6 != "dhcp" }
- ${iface.ipv6}/${iface.ipv6_prefix} # Endereço IPv6 com prefixo
%{ endif }
%{ if iface.ipv4 != "dhcp" && !has_dhcp_interface }
# Configuração de DNS e roteamento para interfaces estáticas
# (Aplicado apenas se não houver interfaces DHCP na VM)
%{ if hostname == "ns1" || hostname == "ns2" }
# Configuração especial para servidores DNS
nameservers:
addresses:
- 127.0.0.1 # Localhost para servidores DNS
- "::1" # Localhost IPv6 para servidores DNS
%{ else }
# Configuração padrão de DNS para outras VMs
nameservers:
addresses:
- ${dmz_config.ns1_v4} # DNS primário IPv4
- ${dmz_config.ns2_v4} # DNS secundário IPv4
- ${dmz_config.ns1_v6} # DNS primário IPv6
- ${dmz_config.ns2_v6} # DNS secundário IPv6
%{ endif }
# Configuração de rotas padrão
routes:
- to: 0.0.0.0/0 # Rota padrão IPv4
via: ${dmz_config.gateway_v4} # Gateway IPv4 da DMZ
%{ if iface.ipv6 != "dhcp" }
- to: "::/0" # Rota padrão IPv6
via: ${dmz_config.gateway_v6} # Gateway IPv6 da DMZ
%{ endif }
%{ endif }
%{ endif }
%{ endfor ~}
# =============================================================================
# NOTAS SOBRE CONFIGURAÇÃO DE REDE
# =============================================================================
#
# LÓGICA DE CONFIGURAÇÃO:
# - Interfaces DHCP: Obtêm configuração automaticamente (IP, DNS, rotas)
# - Interfaces estáticas: Requerem configuração manual de IP, DNS e rotas
# - Servidores DNS (ns1, ns2): Usam localhost como DNS para evitar loops
# - VMs mistas: Se têm interface DHCP, não configuram DNS/rotas nas estáticas
#
# PRECEDÊNCIA DE CONFIGURAÇÃO:
# 1. Se a VM tem pelo menos uma interface DHCP, ela obtém DNS/rotas via DHCP
# 2. Se todas as interfaces são estáticas, configura DNS/rotas manualmente
# 3. Servidores DNS sempre usam localhost independente de outras interfaces
#
# ROTEAMENTO:
# - Rota padrão IPv4: 0.0.0.0/0 via gateway DMZ
# - Rota padrão IPv6: ::/0 via gateway DMZ
# - Rotas específicas podem ser adicionadas conforme necessário
#
# DNS:
# - VMs normais: Usam servidores DNS da DMZ (ns1 e ns2)
# - Servidores DNS: Usam localhost para evitar dependência circular
# - Ordem dos servidores: Primário primeiro, depois secundário
#
# TROUBLESHOOTING:
# - Verifique conectividade: ping para gateway e DNS
# - Teste resolução: nslookup ou dig para domínios conhecidos
# - Verifique rotas: ip route show (IPv4) e ip -6 route show (IPv6)
# - Logs do Cloud-Init: /var/log/cloud-init.log e /var/log/cloud-init-output.log
Explicação:
Este template utiliza a sintaxe do netplan
(versão 2) para configurar as interfaces de rede. Ele é dinâmico e se adapta com base nas propriedades passadas pelo Terraform:
- Loop
for iface in interfaces
: Itera sobre cada interface de rede definida para a VM, configurando-a individualmente. - Configuração DHCP vs. Estática: Usa uma condicional (
%{ if iface.ipv4 == "dhcp" }
) para determinar se a interface deve obter um IP via DHCP ou se deve usar um IP estático. Para IPs estáticos, ele define os endereços IPv4 e IPv6 com seus respectivos prefixos. - Configuração de DNS e Rotas: Para interfaces estáticas (e se a VM não tiver nenhuma interface DHCP), o template configura os servidores DNS e as rotas padrão. Há uma lógica especial para os servidores DNS (
ns1
,ns2
) que os faz usar127.0.0.1
e::1
como DNS, evitando dependências circulares. Para as demais VMs, ele usa os servidores DNS definidos emnetwork_dmz
.
Este template garante que cada VM tenha sua configuração de rede correta aplicada automaticamente no momento da inicialização, seja ela DHCP ou estática, com as rotas e DNS apropriados para a topologia da rede.
cloud-init/user_data.yml
Este template é responsável pela configuração de usuários, chaves SSH e execução de comandos iniciais dentro da máquina virtual. Ele garante que a VM esteja pronta para uso com um usuário administrativo e acesso seguro via SSH.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#cloud-config
# =============================================================================
# TEMPLATE DE CONFIGURAÇÃO DE USUÁRIO CLOUD-INIT
# =============================================================================
# Este template configura usuários, sistema e serviços básicos durante
# a inicialização da VM. Inclui criação de usuário administrativo,
# configuração de SSH e definição de hostname.
# Gerenciamento automático do arquivo /etc/hosts
manage_etc_hosts: true
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE USUÁRIOS
# -----------------------------------------------------------------------------
# Cria o usuário administrativo principal com privilégios sudo e acesso SSH
users:
- name: ${user_name} # Nome do usuário (ex: suporte)
gecos: ${gecos} # Nome completo do usuário
sudo: ALL=(ALL) NOPASSWD:ALL # Privilégios sudo sem senha
groups: ${jsonencode(groups)} # Grupos do usuário (sudo, wheel, etc.)
shell: /bin/bash # Shell padrão
lock_passwd: true # Bloqueia login por senha
ssh_authorized_keys: # Chaves SSH autorizadas
- "${ssh_key}" # Chave pública SSH para acesso
# -----------------------------------------------------------------------------
# CONFIGURAÇÕES DE SEGURANÇA SSH
# -----------------------------------------------------------------------------
# Desabilita métodos de autenticação menos seguros
disable_root: true # Desabilita login direto como root
ssh_pwauth: false # Desabilita autenticação por senha via SSH
# -----------------------------------------------------------------------------
# COMANDOS DE INICIALIZAÇÃO
# -----------------------------------------------------------------------------
# Comandos executados após a configuração inicial do sistema
runcmd:
# Define o hostname da máquina
- hostnamectl set-hostname ${hostname}
# Adiciona entrada no /etc/hosts para resolução local
- echo '127.0.1.1 ${hostname}' >> /etc/hosts
# Comandos adicionais podem ser adicionados aqui:
# - Instalação de pacotes específicos
# - Configuração de serviços
# - Aplicação de patches de segurança
# - Configuração de monitoramento
# =============================================================================
# CONFIGURAÇÕES ADICIONAIS DISPONÍVEIS
# =============================================================================
#
# INSTALAÇÃO DE PACOTES:
# packages:
# - htop
# - vim
# - curl
# - wget
#
# ATUALIZAÇÃO DO SISTEMA:
# package_update: true
# package_upgrade: true
#
# CONFIGURAÇÃO DE TIMEZONE:
# timezone: America/Sao_Paulo
#
# CONFIGURAÇÃO DE LOCALE:
# locale: pt_BR.UTF-8
#
# ESCRITA DE ARQUIVOS:
# write_files:
# - path: /etc/motd
# content: |
# Bem-vindo ao servidor ${hostname}
# Acesso restrito - Use responsavelmente
#
# CONFIGURAÇÃO DE SERVIÇOS:
# runcmd:
# - systemctl enable --now nginx
# - systemctl disable --now apache2
Explicação:
Este template define as configurações de usuário e sistema para as VMs:
manage_etc_hosts: true
: Garante que o Cloud-Init gerencie o arquivo/etc/hosts
da VM.users
: Define o usuário administrativo principal. As propriedadesname
,gecos
,sudo
,groups
,shell
,lock_passwd
essh_authorized_keys
são preenchidas dinamicamente com base nas variáveis passadas pelo Terraform. Osudo: ALL=(ALL) NOPASSWD:ALL
concede privilégios de superusuário sem a necessidade de senha, essh_authorized_keys
injeta a chave pública SSH para acesso seguro.disable_root: true
essh_pwauth: false
: Aumentam a segurança desabilitando o login direto como root e a autenticação por senha via SSH, forçando o uso de chaves SSH.runcmd
: Uma lista de comandos que serão executados após a configuração inicial do sistema. Aqui, o hostname da máquina é definido e uma entrada para o hostname é adicionada ao/etc/hosts
.
Este template automatiza a criação de usuários e a configuração inicial de segurança, tornando as VMs prontas para uso e acessíveis via SSH imediatamente após o provisionamento.
Configuração Alternativa: Autenticação por Senha
Embora a autenticação por chave SSH seja a mais recomendada para ambientes de produção devido à sua segurança, pode haver cenários (como ambientes de desenvolvimento ou testes) onde o acesso por senha é desejado. O Cloud-Init permite configurar isso com algumas modificações no user_data.yml
.
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#cloud-config
# =============================================================================
# TEMPLATE ALTERNATIVO COM AUTENTICAÇÃO POR SENHA
# =============================================================================
# Esta configuração permite login tanto por chave SSH quanto por senha,
# útil para ambientes de desenvolvimento ou quando o acesso por chave
# não está disponível.
manage_etc_hosts: true
# Habilita autenticação por senha via SSH
ssh_pwauth: true
# -----------------------------------------------------------------------------
# CONFIGURAÇÃO DE USUÁRIO COM SENHA
# -----------------------------------------------------------------------------
users:
- name: ${user_name} # Nome do usuário
gecos: ${gecos} # Nome completo do usuário
sudo: ALL=(ALL) NOPASSWD:ALL # Privilégios sudo sem senha
groups: ${jsonencode(groups)} # Grupos do usuário
shell: /bin/bash # Shell padrão
lock_passwd: false # Permite login por senha
passwd: ${user_password} # Hash da senha (gerado com mkpasswd)
ssh_authorized_keys: # Chaves SSH (ainda funcionam)
- "${ssh_key}"
# -----------------------------------------------------------------------------
# COMANDOS DE INICIALIZAÇÃO
# -----------------------------------------------------------------------------
runcmd:
- hostnamectl set-hostname ${hostname}
- echo '127.0.1.1 ${hostname}' >> /etc/hosts
# =============================================================================
# NOTAS DE SEGURANÇA PARA CONFIGURAÇÃO COM SENHA
# =============================================================================
#
# GERAÇÃO DE SENHA SEGURA:
# - Use: mkpasswd --method=SHA-512
# - Exemplo: mkpasswd --method=SHA-512 "MinhaSenh@123"
# - O resultado é um hash que deve ser usado em ${user_password}
#
# CONSIDERAÇÕES DE SEGURANÇA:
# - Senhas devem ser complexas (maiúsculas, minúsculas, números, símbolos)
# - Considere usar apenas em ambientes de desenvolvimento
# - Para produção, prefira autenticação apenas por chave SSH
# - Monitore tentativas de login por senha nos logs
#
# CONFIGURAÇÕES ADICIONAIS DE SSH:
# - Considere alterar a porta padrão do SSH (22)
# - Configure fail2ban para proteção contra ataques de força bruta
# - Use autenticação de dois fatores quando possível
Explicação das Modificações:
ssh_pwauth: true
: Habilita a autenticação por senha para o SSH.lock_passwd: false
: Permite o login por senha para o usuário.passwd: ${user_password}
: Adiciona o hash da senha do usuário. É crucial que este hash seja gerado de forma segura (ex: usandomkpasswd --method=SHA-512
) e não a senha em texto claro. Você precisaria adicionar uma nova variáveluser_password
emvariables.tf
e fornecer seu valor emterraform.tfvars
se for usar esta abordagem.
Recomendação de Segurança:
Para ambientes de produção, sempre prefira a autenticação por chave SSH. A autenticação por senha, mesmo com senhas fortes, é mais suscetível a ataques de força bruta. Se for usar senhas, certifique-se de que sejam complexas e considere implementar medidas adicionais de segurança, como fail2ban
e autenticação de dois fatores.
Instruções para Geração de Credenciais
Para que o Cloud-Init possa configurar o acesso às suas VMs de forma segura, você precisará gerar uma chave SSH e, opcionalmente, uma senha criptografada.
Geração de Senha Criptografada (Opcional)
Se você optar por usar a configuração alternativa com senha, precisará gerar um hash da senha. Nunca use a senha em texto claro no seu código Terraform!
1
2
3
4
5
6
7
8
# Instale o pacote 'whois' se 'mkpasswd' não estiver disponível
sudo apt update && sudo apt install -y whois
# Gera hash de senha compatível com Cloud-Init
mkpasswd --method=SHA-512 "SuaSenhaSegura123"
# Exemplo de saída:
# $6$rounds=656000$YourSaltHere$HashedPasswordHere...
Copie o hash gerado e use-o no terraform.tfvars
(se você adicionar a variável user_password
).
Geração de Chave SSH
É altamente recomendado usar chaves SSH para acesso seguro às suas VMs. Se você ainda não tem um par de chaves, pode gerá-lo da seguinte forma:
1
2
3
4
5
6
7
8
# Gera par de chaves SSH Ed25519 (recomendado por ser mais seguro e moderno)
ssh-keygen -t ed25519 -C "seu_email@exemplo.com" -f ~/.ssh/tfvms
# Gera par de chaves SSH RSA (alternativa, menos moderna)
ssh-keygen -t rsa -b 4096 -C "seu_email@exemplo.com" -f ~/.ssh/tfvms
# Exibe a chave pública para uso no terraform.tfvars
cat ~/.ssh/tfvms.pub
Copie o conteúdo da chave pública (o que for exibido pelo comando cat ~/.ssh/tfvms.pub
) e cole-o no terraform.tfvars
na variável ssh_public_key
.
Implantação da Infraestrutura com Terraform
Com todos os arquivos de configuração prontos, você pode agora implantar sua infraestrutura KVM/LIBVIRT usando os comandos do Terraform.
- Inicialize o Diretório Terraform: Navegue até o diretório raiz do seu projeto Terraform (onde estão os arquivos
.tf
) e execute o comando de inicialização. Isso fará o download dos provedores necessários (como olibvirt
).1
terraform init
- Planeje a Implantação: O comando
terraform plan
permite que você visualize as alterações que o Terraform fará em sua infraestrutura antes de aplicá-las. É uma boa prática revisar este plano para garantir que tudo esteja conforme o esperado.1
terraform plan
- Aplique as Alterações: Uma vez satisfeito com o plano, execute o comando
terraform apply
para provisionar os recursos definidos. O Terraform solicitará uma confirmação antes de prosseguir.1
terraform apply
Se você quiser pular a confirmação (não recomendado para produção), pode usar:
1
terraform apply -auto-approve
Verificação e Acesso às VMs
Após a conclusão do terraform apply
, suas máquinas virtuais estarão provisionadas e configuradas. Você pode verificar o status e acessá-las da seguinte forma:
- Listar VMs no Libvirt: Use o comando
virsh list --all
para ver todas as máquinas virtuais gerenciadas pelo Libvirt.1
virsh list --all
- Obter Endereços IP das VMs: Os endereços IP das VMs podem ser obtidos de várias maneiras. Uma forma é inspecionar os logs do Cloud-Init ou, se a VM já estiver em execução, usar
virsh domifaddr <nome_da_vm>
(pode levar um tempo para o DHCP atribuir o IP).1 2
# Exemplo para obter o IP do gateway virsh domifaddr gateway
Alternativamente, você pode verificar os outputs do Terraform se tiver configurado:
1 2 3
output "vm_ips" { value = { for k, v in libvirt_domain.domain : k => v.network_interface[0].addresses[0] } }
E então executar
terraform output vm_ips
. - Acessar as VMs via SSH: Com a chave SSH configurada e o IP da VM, você pode acessá-la via SSH. Lembre-se que o usuário padrão criado pelo Cloud-Init é
suporte
(ou o que você definiu emdefault_vm_user
).1
ssh -i ~/.ssh/tfvms suporte@IP_DA_VM
Para facilitar o acesso, você pode configurar seu arquivo
~/.ssh/config
:1 2 3 4
echo "Host vm-* User suporte IdentityFile ~/.ssh/tfvms StrictHostKeyChecking no" >> ~/.ssh/config
Após isso, você pode se conectar usando um nome mais amigável, por exemplo:
1
ssh vm-gateway
- Destruir a Infraestrutura (Opcional): Quando você não precisar mais da infraestrutura, pode destruí-la completamente com um único comando Terraform. ATENÇÃO: Este comando removerá IRREVERSIVELMENTE todas as VMs, volumes e redes criadas. Use com extrema cautela e certifique-se de que não há dados importantes ou dependências nesta infraestrutura antes de executar!
1
terraform destroy
Assim como o
apply
, você pode usar-auto-approve
para pular a confirmação.
Com estas instruções, você pode implantar, verificar e gerenciar sua infraestrutura KVM/LIBVIRT de forma eficiente usando Terraform.
Diagrama de Rede
Para uma melhor compreensão da topologia de rede proposta, um diagrama visual seria extremamente útil. Abaixo, uma representação simplificada da arquitetura de rede, que pode ser expandida para incluir todos os componentes e fluxos de tráfego.
graph TD
subgraph Internet
A[External Network]
end
subgraph KVM Host
B(Libvirt External Network) -- NAT --> A
B -- eth0 --> C[Gateway VM]
subgraph DMZ
D(Libvirt DMZ Network) -- eth1 --> C
D -- eth0 --> E[NS1 VM]
D -- eth0 --> F[NS2 VM]
end
subgraph CGR
G(Libvirt CGR Network) -- eth2 --> C
G -- eth0 --> H[DB1 VM (Exemplo)]
end
subgraph DHCP
I(Libvirt DHCP Network) -- eth3 --> C
I -- eth0 --> J[Client VM (Exemplo)]
end
C -- eth1 --> D
C -- eth2 --> G
C -- eth3 --> I
end
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#ccf,stroke:#333,stroke-width:2px
style D fill:#fcf,stroke:#333,stroke-width:2px
style E fill:#ccf,stroke:#333,stroke-width:2px
style F fill:#ccf,stroke:#333,stroke-width:2px
style G fill:#ccf,stroke:#333,stroke-width:2px
style H fill:#ccf,stroke:#333,stroke-width:2px
style I fill:#bbf,stroke:#333,stroke-width:2px
style J fill:#ccf,stroke:#333,stroke-width:2px
Explicação do Diagrama:
- External Network: Representa a conexão com a internet.
- Libvirt External Network: Rede virtual do Libvirt com NAT para acesso à internet.
- Gateway VM: Máquina virtual que atua como roteador e firewall, conectando todas as redes internas à rede externa.
- Libvirt DMZ Network: Rede isolada para serviços públicos (como NS1 e NS2).
- NS1 VM / NS2 VM: Máquinas virtuais de servidores DNS primário e secundário.
- Libvirt CGR Network: Rede isolada para gerenciamento e acesso administrativo (ex: DB1 VM).
- DB1 VM (Exemplo): Máquina virtual de banco de dados, acessível apenas pela rede CGR.
- Libvirt DHCP Network: Rede para clientes que obtêm IP via DHCP.
- Client VM (Exemplo): Máquina virtual cliente que obtém IP via DHCP.
Este diagrama ajuda a visualizar o isolamento e a comunicação entre as diferentes zonas da infraestrutura, reforçando a importância do Gateway VM
como ponto central de controle de tráfego.
Outputs do Terraform
Para facilitar a obtenção de informações sobre os recursos criados, como os endereços IP das VMs, você pode definir outputs
no seu código Terraform. Crie um arquivo outputs.tf
com o seguinte conteúdo:
1
2
3
4
5
6
7
8
9
10
11
12
# outputs.tf
output "vm_ips" {
description = "Endereços IP das máquinas virtuais criadas"
value = {
for vm_name, vm_config in libvirt_domain.domain :
vm_name => {
for iface in vm_config.network_interface :
iface.hostname => iface.addresses
}
}
}
Após aplicar as alterações com terraform apply
, você pode executar terraform output vm_ips
para ver uma lista dos IPs de todas as VMs.
Gerenciamento de Estado (State Management)
O Terraform mantém um arquivo de estado (terraform.tfstate
) que mapeia os recursos reais da sua infraestrutura para a sua configuração. Este arquivo é crucial para o funcionamento do Terraform, pois ele usa o estado para determinar quais alterações precisam ser feitas para alcançar o estado desejado.
Considerações Importantes:
- Estado Local: Por padrão, o Terraform armazena o arquivo de estado localmente no diretório do projeto. Para projetos pequenos e individuais, isso pode ser suficiente.
- Estado Remoto: Para equipes e ambientes de produção, é altamente recomendado configurar um backend remoto para o estado (ex: S3, Azure Blob Storage, Terraform Cloud). Isso oferece:
- Colaboração: Permite que múltiplos usuários trabalhem no mesmo projeto sem conflitos.
- Bloqueio de Estado: Previne que múltiplos
terraform apply
sejam executados simultaneamente, evitando corrupção do estado. - Histórico de Versões: Mantém um histórico de todas as alterações no estado, facilitando a recuperação em caso de erros.
- Segurança: Armazena o estado em um local seguro e criptografado.
Exemplo de Configuração de Backend S3 (em providers.tf
):
1
2
3
4
5
6
7
8
9
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "kvm-libvirt/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "my-terraform-state-lock"
}
}
Nota: A configuração de um backend remoto deve ser feita antes do primeiro terraform apply
. Se você já tem um estado local, precisará migrá-lo para o backend remoto.
Considerações de Performance para KVM/Libvirt
Na seção domain.tf
, o uso de cpu { mode = "host-passthrough" }
já é um excelente passo para otimizar a performance da CPU. No entanto, outras otimizações podem ser aplicadas para melhorar o desempenho geral das máquinas virtuais:
- Drivers VirtIO: Para discos e interfaces de rede, é altamente recomendado usar os drivers VirtIO. Eles são drivers paravirtualizados que oferecem melhor desempenho em comparação com a emulação de hardware tradicional. Certifique-se de que suas imagens de Cloud-Init incluam os módulos
virtio
necessários.- Exemplo de configuração de disco com VirtIO (em
domain.tf
):1 2 3 4
disk { volume_id = libvirt_volume.os_image[each.key].id target_bus = "virtio" # Define o barramento como VirtIO }
- Exemplo de configuração de interface de rede com VirtIO (em
domain.tf
):1 2 3 4 5
network_interface { network_name = network_interface.value.name model = "virtio" # Define o modelo da interface como VirtIO # ... outras configurações }
- Exemplo de configuração de disco com VirtIO (em
- IO Thread: Para cargas de trabalho intensivas em I/O, configurar um
IO thread
dedicado para os volumes de disco pode melhorar significativamente o desempenho. Isso permite que as operações de I/O sejam processadas em um thread separado, reduzindo a latência.- Exemplo de configuração de IO Thread (em
domain.tf
):1 2 3 4 5
disk { volume_id = libvirt_volume.os_image[each.key].id target_bus = "virtio" io_thread = "1" # Associa o disco ao IO thread 1 }
- Nota: Você precisaria definir o
io_thread
no recursolibvirt_domain
também, se estiver usando múltiplos threads de I/O.
- Exemplo de configuração de IO Thread (em
- Cache de Disco: A configuração do cache de disco pode ter um grande impacto no desempenho. As opções comuns são
writeback
(melhor desempenho, maior risco de perda de dados em caso de falha),writethrough
(equilíbrio entre desempenho e segurança) enone
(mais seguro, menor desempenho).- Exemplo de configuração de cache (em
domain.tf
):1 2 3 4 5
disk { volume_id = libvirt_volume.os_image[each.key].id target_bus = "virtio" cache = "writeback" # Ou "writethrough", "none" }
- Exemplo de configuração de cache (em
Ao aplicar essas otimizações, você pode garantir que suas máquinas virtuais KVM/Libvirt operem com a máxima eficiência, especialmente em ambientes de produção com altas demandas de recursos.
Configuração de Firewall no Gateway
O tutorial menciona que o servidor gateway
atuará como roteador e firewall. Para garantir o isolamento e a segurança das redes internas, é crucial configurar regras de firewall adequadas dentro da VM gateway
. Isso complementa o isolamento de rede fornecido pelo Libvirt e controla o tráfego entre as diferentes zonas (DMZ, CGR, DHCP) e a rede externa.
Ferramentas Comuns de Firewall no Linux:
iptables
/nftables
: Ferramentas de linha de comando para configurar o firewall do kernel Linux.nftables
é a sucessora moderna doiptables
.firewalld
: Um daemon que fornece uma interface dinâmica para gerenciar o firewall, facilitando a configuração de zonas e serviços.
Exemplo Simplificado de Regras (usando iptables
para ilustrar o conceito):
Este é um exemplo conceitual. As regras exatas dependerão da sua política de segurança.
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
31
32
33
34
35
36
37
38
39
40
41
# Exemplo de script de firewall (a ser executado dentro da VM gateway)
# Limpa regras existentes
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Permite tráfego de loopback
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# Permite tráfego já estabelecido ou relacionado
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Habilita NAT para a rede externa (se aplicável)
iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
# Permite SSH de redes específicas (ex: CGR)
iptables -A INPUT -i ens5 -p tcp --dport 22 -j ACCEPT
iptables -A FORWARD -i ens5 -o ens4 -p tcp --dport 22 -j ACCEPT # CGR para DMZ (SSH)
# Permite tráfego DNS da DMZ para a rede externa
iptables -A FORWARD -i ens4 -p udp --dport 53 -j ACCEPT
iptables -A FORWARD -i ens4 -p tcp --dport 53 -j ACCEPT
# Bloqueia todo o resto
iptables -A INPUT -j LOG --log-prefix "IPTABLES_DROP: "
iptables -A INPUT -j DROP
iptables -A FORWARD -j LOG --log-prefix "IPTABLES_FORWARD_DROP: "
iptables -A FORWARD -j DROP
# Salvar regras (depende da distribuição, ex: netfilter-persistent para Debian/Ubuntu)
# sudo apt install -y netfilter-persistent
# sudo netfilter-persistent save
Recomendações:
- Política de Segurança: Defina uma política de segurança clara antes de implementar as regras de firewall. O que deve ser permitido? O que deve ser negado?
- Menos Privilégio: Adote o princípio do menor privilégio, permitindo apenas o tráfego essencial e bloqueando todo o resto.
- Logging: Configure o logging para o tráfego bloqueado para monitorar tentativas de acesso não autorizado.
- Automatização: Integre a configuração do firewall ao seu processo de automação (ex: via Cloud-Init
runcmd
ou uma ferramenta de gerenciamento de configuração como Ansible) para garantir consistência e repetibilidade. - Testes: Teste exaustivamente suas regras de firewall para garantir que elas funcionem conforme o esperado e não bloqueiem tráfego legítimo.
Conclusão
Este tutorial demonstrou como utilizar o Terraform para automatizar a criação e gerenciamento de uma infraestrutura de virtualização KVM/LIBVIRT. Através da modularização dos arquivos de configuração e do uso inteligente do Cloud-Init, é possível provisionar ambientes complexos de forma rápida, consistente e repetível.
Compreender a função de cada arquivo (providers.tf
, variables.tf
, network.tf
, volumes.tf
, cloudinit.tf
, domain.tf
, terraform.tfvars
, servers.auto.tfvars
, networks.auto.tfvars
) e a interação entre eles é fundamental para adaptar e expandir esta infraestrutura às suas necessidades específicas. A automação com Terraform não só economiza tempo, mas também reduz erros manuais, garantindo que seus ambientes virtuais sejam sempre provisionados de acordo com as melhores práticas e suas definições.
Esperamos que este guia detalhado sirva como um ponto de partida sólido para suas implementações de infraestrutura como código com Terraform e KVM/LIBVIRT.