Table of Contents

Criação de uma Máquina Virtual em Ambiente de Virtualização KVM com Terraform

Este tutorial demonstra como criar uma máquina virtual (VM) utilizando KVM e Terraform, assumindo que o Terraform, KVM e o provider libvirt já estão instalados.

Pré-requisitos

Antes de começar, certifique-se de que:

1. Definir Variáveis e Configuração do Terraform

Crie um arquivo main.tf com o seguinte conteúdo:

# Definindo variáveis para configuração da VM
variable "vm_template" {
  type = map(any)
}
 
# # Define os provedores necessários para o Terraform.
terraform {
  required_providers {
    libvirt = {
      source  = "dmacvicar/libvirt"
    }
  }
}
 
# Configura o provedor libvirt com a URI do sistema QEMU.
provider "libvirt" {
  uri = "qemu:///system"
}

Aqui, o provider libvirt será usado para gerenciar a VM via KVM.

2. Criar os Volumes da VM

# Volume de imagem do sistema operacional
resource "libvirt_volume" "os_image" {
  name   = var.vm_template["os_image_name"]
  pool   = var.vm_template["storage_pool"]
  source = var.vm_template["os_image_url"]
  format = "qcow2"
}
 
# Volume de dados da VM
resource "libvirt_volume" "os_datas" {
  name           = var.vm_template["os_volume_name"]
  base_volume_id = libvirt_volume.os_image.id
  pool           = var.vm_template["storage_pool"]
  size           = var.vm_template["disksize"] * 1024 * 1024 * 1024 # Converte para bytes
}

Esse trecho define os volumes usados pela VM, especificando a imagem do SO e o volume de dados.

3. Definir o Disco cloud-init

# Definir o arquivo cloud-init para automação
data "template_file" "user_data" {
  template = file("${path.module}/${var.vm_template["cloud_init_file"]}")
}
 
resource "libvirt_cloudinit_disk" "cloudinit" {
  name      = "cloudinit.iso"
  user_data = data.template_file.user_data.rendered
  pool      = var.vm_template["storage_pool"]
}

O cloud-init permite automatizar a configuração inicial da VM, como a criação de usuários e a instalação de pacotes.

4. Configurar o Domínio (VM)

resource "libvirt_domain" "domain" {
  name   = var.vm_template["name"]
  memory = var.vm_template["memory"]
  vcpu   = var.vm_template["cpu"]
 
  cpu {
    mode = "host-passthrough"
  }
 
  cloudinit = libvirt_cloudinit_disk.cloudinit.id
 
  network_interface {
    network_name   = var.vm_template["network_name"]
    wait_for_lease = true
  }
 
  console {
    type        = "pty"
    target_type = "virtio"
    target_port = "1"
  }
 
  disk {
    volume_id = libvirt_volume.os_datas.id
  }
 
  graphics {
    type        = "spice"
    listen_type = "address"
    autoport    = true
  }
}
 
output "ip" {
  value = libvirt_domain.domain.network_interface[0].addresses[0]
}

Este trecho define a configuração principal da VM, incluindo CPU, memória, rede e interface gráfica.

5. Variáveis de Configuração

Crie um arquivo `variables.tf` ou defina diretamente no `main.tf`:

vm_template = {
  name            = "tf-vm-01"
  cpu             = 2
  memory          = 2048
  disksize        = 32
  storage_pool    = "default"
  os_image_name   = "tf_vm_01_image.qcow2"
  os_volume_name  = "tf_vm_01_volume.qcow2"
  network_name    = "default"
  cloud_init_file = "cloud-init.yml"
  os_image_url    = "/home/user/kvm/templates/debian-12-generic-amd64.qcow2"
}

Altere os valores conforme necessário para a sua infraestrutura.

6. Arquivo cloud-init

Crie um arquivo cloud-init.yml para automatizar a configuração da VM:

Definição para acesso ssh por senha

cloud-init.yml
#cloud-config

hostname: vm-tf

ssh_pwauth: yes  # Habilita login por senha para usuários

users:
  - name: gean
    sudo: ALL=(ALL) NOPASSWD:ALL  # Permite 'gean' usar sudo sem senha
    groups: users, sudo
    shell: /bin/bash
    lock_passwd: false  # Desbloqueia o usuário 'gean' para usar senhas
    passwd: $6$uGQol.HnKU0nvpEy$YJl94Y7/p1cWZVlu0gsZIeebssh4oHCIQ9VNX721T1/Lx0UbQVbjfbzS2.9.2EGz4Hdxi0ICNKAua8lo/AsuT1  # Senha criptografada de 'gean'

chpasswd:
  list: |
    root:$6$uGQol.HnKU0nvpEy$YJl94Y7/p1cWZVlu0gsZIeebssh4oHCIQ9VNX721T1/Lx0UbQVbjfbzS2.9.2EGz4Hdxi0ICNKAua8lo/AsuT1  # Senha criptografada de root
  expire: False  # Define que as senhas não expiram

packages:
  - qemu-guest-agent  # Instala o agente QEMU para máquinas virtuais
  - bash-completion  # Completa comandos no Bash

package_update: true  # Atualiza lista de pacotes
package_upgrade: true  # Atualiza os pacotes instalados
 
# Definir a localidade e fuso horário (opcional)
locale: pt_BR.UTF-8
timezone: America/Sao_Paulo

Esse arquivo define o hostname, usuários, senha root, e instala pacotes.

Configuração para acesso por ssh senha senha

main.tf
# vim main.tf 
# Definindo variáveis para configuração da VM
variable "vm_template" {
  type = map(any)
}
 
terraform {
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
    }
  }
}
 
provider "libvirt" {
  uri = "qemu:///system"
}
 
# Volume de imagem do sistema operacional
resource "libvirt_volume" "os_image" {
  name   = var.vm_template["os_image_name"]
  pool   = var.vm_template["storage_pool"]
  source = var.vm_template["os_image_url"]
  format = "qcow2"
}
 
# Volume de dados da VM
resource "libvirt_volume" "os_datas" {
  name           = var.vm_template["os_volume_name"]
  base_volume_id = libvirt_volume.os_image.id
  pool           = var.vm_template["storage_pool"]
  size           = var.vm_template["disksize"] * 1024 * 1024 * 1024 # Converte para bytes
}
 
# Definir o arquivo cloud-init para automação
 
data "template_file" "user_data" {
  template = file("${path.module}/${var.vm_template["cloud_init_file"]}")
 
  vars = {
    hostname = var.vm_template["hostname"]
    ssh_key  = var.vm_template["ssh_key"]
  }
}
 
 
# Criando disco Cloud-Init
resource "libvirt_cloudinit_disk" "cloudinit" {
  name      = "cloudinit.iso"
  user_data = data.template_file.user_data.rendered
  pool      = var.vm_template["storage_pool"]
}
 
# Criando a VM com libvirt
resource "libvirt_domain" "domain" {
  name   = var.vm_template["name"]
  memory = var.vm_template["memory"]
  vcpu   = var.vm_template["cpu"]
 
  cpu {
    mode = "host-passthrough"
  }
 
  cloudinit = libvirt_cloudinit_disk.cloudinit.id
 
  network_interface {
    network_name   = var.vm_template["network_name"]
    wait_for_lease = true
  }
 
  console {
    type        = "pty"
    target_type = "virtio"
    target_port = "1"
  }
 
  disk {
    volume_id = libvirt_volume.os_datas.id
  }
 
  graphics {
    type        = "spice"
    listen_type = "address"
    autoport    = true
  }
}
 
output "ip" {
  value = libvirt_domain.domain.network_interface[0].addresses[0]
}
cloud-init.yml
# vim cloud-init.yml 
#cloud-config
 
# Configuração do Hostname
hostname: ${hostname}
 
# Criação de usuário
users:
  - name: gean
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: users, sudo
    shell: /bin/bash
    ssh-authorized-keys:
      - ${ssh_key}
 
# Definir senha root
chpasswd:
  list: |
    root:$6$R4tXC5apTx$f4WVAylB/SZ/0ppE7Zp4lurvzAhcm.BaU3xJKaoESu7cv13sR7RYVkjVQwxtvA9/vUggWu/a9N0L9EP1lg/Ez1
  expire: False
 
# Pacotes a serem instalados
packages:
  - qemu-guest-agent
  - bash-completion
 
# Atualização do sistema
package_update: true
package_upgrade: true
 
# Localidade e fuso horário
locale: pt_BR.UTF-8
timezone: America/Sao_Paulo
 
# Logs de inicialização para depuração
final_message: "A VM foi provisionada corretamente!"
terraform.tfvars
$ cat terraform.tfvars 
vm_template = {
  name            = "tf-vm-01"
  cpu             = 2
  memory          = 2048
  disksize        = 32
  storage_pool    = "default"
  hostname        = "tf-vm-01"
  os_image_name   = "tf_vm_01_image.qcow2"
  os_volume_name  = "tf_vm_01_volume.qcow2"
  network_name    = "default"
  cloud_init_file = "cloud-init.yml"
  os_image_url    = "/home/gean/kvm/templates/ubuntu-22.04-server-cloudimg-amd64.img"
  ssh_key         = "ssh-ed25519 AAAA..."
}

7. Gerar Senha e Chave SSH

mkpasswd --method=SHA-512
ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/tfvms

8. Construção da Infraestrutura

Execute os comandos Terraform:

terraform init        # Inicializar o Terraform
terraform fmt         # Formatar arquivos
terraform validate    # Validar a configuração
terraform plan        # Planejar a infraestrutura
terraform apply       # Aplicar a infraestrutura

9. Outras Opções

terraform apply -var="vm_name=custom_name" -var="vm_memory=4096"
terraform apply -var-file="custom_variables.tfvars"

Conclusão

Este tutorial fornece um processo completo para criar uma VM no KVM usando Terraform, com foco em automação e personalização.