====== Infraestrutura KVM usando Terraform para Laboratório com Ansible ====== Este tutorial fornece as etapas para criar uma infraestrutura de laboratório em KVM usando Terraform e preparar as VMs para serem gerenciadas via Ansible. As máquinas virtuais terão diferentes sistemas operacionais, incluindo distribuições Debian/Ubuntu e RHEL, e serão configuradas automaticamente usando Cloud-init. ===== Requisitos ===== - **KVM** instalado no host. - **Terraform** para provisionamento de infraestrutura. - Templates de imagens **Ubuntu 22.04**, **Debian 12**, e **Oracle Linux 9**. ===== Arquivos principais ===== ==== main.tf ==== Este arquivo contém a configuração principal para criar a infraestrutura KVM, definindo a rede, volumes e as VMs. terraform { required_providers { libvirt = { source = "dmacvicar/libvirt" } } } provider "libvirt" { uri = "qemu:///system" } variable "vms" { type = list(map(any)) } resource "libvirt_network" "ansible" { name = "ansible" mode = "nat" addresses = ["10.3.4.0/24"] autostart = true dhcp { enabled = false } dns { enabled = true } } resource "libvirt_volume" "os_image" { for_each = { for vm in var.vms : vm.name => vm } name = each.value.os_image_name pool = each.value.storage_pool source = each.value.os_image_url format = "qcow2" } resource "libvirt_volume" "os_datas" { for_each = { for vm in var.vms : vm.name => vm } name = each.value.os_datas_name base_volume_id = libvirt_volume.os_image[each.key].id pool = each.value.storage_pool size = each.value.disksize * 1024 * 1024 * 1024 // GB para bytes } data "template_file" "user_data_deb" { for_each = { for vm in var.vms : vm.name => vm if lookup(vm, "user_data_deb", null) != null } template = file("${path.module}/${each.value.user_data_deb}") vars = { hostname = each.value.hostname } } data "template_file" "user_data_rhel" { for_each = { for vm in var.vms : vm.name => vm if lookup(vm, "user_data_rhel", null) != null } template = file("${path.module}/${each.value.user_data_rhel}") vars = { hostname = each.value.hostname } } data "template_file" "network_config_deb" { for_each = { for vm in var.vms : vm.name => vm if lookup(vm, "network_config_deb", null) != null } template = file("${path.module}/${each.value.network_config_deb}") vars = { network_ip = each.value.network_ip } } data "template_file" "network_config_rhel" { for_each = { for vm in var.vms : vm.name => vm if lookup(vm, "network_config_rhel", null) != null } template = file("${path.module}/${each.value.network_config_rhel}") vars = { network_ip = each.value.network_ip } } resource "libvirt_cloudinit_disk" "cloudinit_deb" { for_each = { for vm in var.vms : vm.name => vm if lookup(vm, "user_data_deb", null) != null } name = "${each.key}_cloudinit.iso" user_data = data.template_file.user_data_deb[each.key].rendered network_config = data.template_file.network_config_deb[each.key].rendered pool = each.value.storage_pool } resource "libvirt_cloudinit_disk" "cloudinit_rhel" { for_each = { for vm in var.vms : vm.name => vm if lookup(vm, "user_data_rhel", null) != null } name = "${each.key}_cloudinit.iso" user_data = data.template_file.user_data_rhel[each.key].rendered network_config = data.template_file.network_config_rhel[each.key].rendered pool = each.value.storage_pool } locals { cloudinit_disks = { for vm in var.vms : vm.name => ( lookup(vm, "user_data_deb", null) != null ? libvirt_cloudinit_disk.cloudinit_deb[vm.name].id : libvirt_cloudinit_disk.cloudinit_rhel[vm.name].id ) } } resource "libvirt_domain" "domain" { for_each = { for vm in var.vms : vm.name => vm } name = each.value.name memory = each.value.memory vcpu = each.value.cpu cpu { mode = "host-passthrough" } cloudinit = local.cloudinit_disks[each.key] network_interface { network_name = each.value.network_name } disk { volume_id = libvirt_volume.os_datas[each.key].id } console { type = "pty" target_type = "virtio" target_port = "1" } graphics { type = "spice" listen_type = "address" autoport = true } } ==== terraform.tfvars ==== Configura as variáveis para definir as VMs no ambiente. vms = [ { name = "ansible" cpu = 2 memory = 4096 disksize = 32 storage_pool = "default" hostname = "ansible" os_image_name = "ansible_image.qcow2" os_datas_name = "ansible_datas.qcow2" network_name = "ansible" user_data_deb = "cloud-init-deb.yml" network_config_deb = "network-config-deb.yml" network_ip = "10.3.4.10" os_image_url = "/home/gean/kvm/templates/ubuntu-22.04-server-cloudimg-amd64.img" }, { name = "balancer" cpu = 2 memory = 2048 disksize = 64 storage_pool = "default" hostname = "balancer" os_image_name = "balancer_image.qcow2" os_datas_name = "balancer_datas.qcow2" network_name = "ansible" user_data_rhel = "cloud-init-rhel.yml" network_config_rhel = "network-config-rhel.yml" network_ip = "10.3.4.11" os_image_url = "/home/gean/kvm/templates/OL9U3_x86_64-kvm-b220.qcow2" }, { name = "webserver1" cpu = 2 memory = 2048 disksize = 32 storage_pool = "default" hostname = "webserver1" os_image_name = "webserver1_image.qcow2" os_datas_name = "webserver1_datas.qcow2" network_name = "ansible" user_data_deb = "cloud-init-deb.yml" network_config_deb = "network-config-deb.yml" network_ip = "10.3.4.12" os_image_url = "/home/gean/kvm/templates/ubuntu-22.04-server-cloudimg-amd64.img" }, { name = "webserver2" cpu = 2 memory = 2048 disksize = 64 storage_pool = "default" hostname = "webserver2" os_image_name = "webserver2_image.qcow2" os_datas_name = "webserver2_datas.qcow2" network_name = "ansible" user_data_rhel = "cloud-init-rhel.yml" network_config_rhel = "network-config-rhel.yml" network_ip = "10.3.4.13" os_image_url = "/home/gean/kvm/templates/OL9U3_x86_64-kvm-b220.qcow2" }, { name = "dbserver" cpu = 2 memory = 2048 disksize = 32 storage_pool = "default" hostname = "dbserver" os_image_name = "dbserver_image.qcow2" os_datas_name = "dbserver_datas.qcow2" network_name = "ansible" user_data_deb = "cloud-init-deb.yml" network_config_deb = "network-config-deb.yml" network_ip = "10.3.4.14" os_image_url = "/home/gean/kvm/templates/debian-12-generic-amd64.qcow2" } ] ==== cloud-init-deb.yml ==== Configuração de inicialização para as VMs baseadas em Debian/Ubuntu. #cloud-config hostname: ${hostname} ssh_pwauth: yes users: - name: gean sudo: ALL=(ALL) NOPASSWD:ALL groups: users, sudo shell: /bin/bash lock_passwd: false passwd: $6$uGQol.HnKU0nvpEy$YJl94Y7/p1cWZVlu0gsZIeebssh4oHCIQ9VNX721T1/Lx0UbQVbjfbzS2.9.2EGz4Hdxi0ICNKAua8lo/AsuT1 chpasswd: list: | root:root expire: False packages: - qemu-guest-agent - bash-completion package_update: true package_upgrade: true ==== cloud-init-rhel.yml ==== Configuração de inicialização para as VMs baseadas em RHEL. #cloud-config ssh_pwauth: yes users: - name: gean sudo: ALL=(ALL) NOPASSWD:ALL groups: users, whell shell: /bin/bash lock_passwd: false passwd: $6$uGQol.HnKU0nvpEy$YJl94Y7/p1cWZVlu0gsZIeebssh4oHCIQ9VNX721T1/Lx0UbQVbjfbzS2.9.2EGz4Hdxi0ICNKAua8lo/AsuT1 chpasswd: list: | root:root expire: False packages: - qemu-guest-agent - bash-completion package_update: true package_upgrade: true runcmd: - systemctl enable qemu-guest-agent - systemctl start qemu-guest-agent - hostnamectl set-hostname ${hostname} ==== network-config-deb.yml ==== Configuração de rede para VMs baseadas em Debian/Ubuntu. #cloud-config network: version: 2 ethernets: ens3: addresses: - ${network_ip}/24 nameservers: addresses: - 10.3.4.1 routes: - to: default via: 10.3.4.1 ==== network-config-rhel.yml ==== Configuração de rede para VMs baseadas em RHEL. #cloud-config network: version: 1 config: - type: physical name: eth0 subnets: - type: static address: ${network_ip}/24 gateway: 10.3.4.1 ===== Passos para execução ===== - **Instale o KVM** e **Terraform** em seu ambiente. [[https://geanmartins.com.br/posts/terraform-kvm/#criando-uma-m%C3%A1quina-virtual|Instalação e Configuração: KVM e Terraform]] - Copie todos os arquivos para um diretório de trabalho. - Execute os seguintes comandos para provisionar a infraestrutura: terraform init terraform fmt terraform validate terraform plan terraform apply 4. O Terraform criará as VMs com base na configuração e aplicará as configurações de rede e de sistema. ===== Considerações Finais ===== - Ajuste os arquivos conforme suas necessidades específicas. - Verifique se as imagens das VMs estão corretamente localizadas e acessíveis.